<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://alaggydev.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://alaggydev.github.io/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-05-05T23:15:55+08:00</updated><id>https://alaggydev.github.io/feed.xml</id><title type="html">Laggy’s Blog</title><subtitle>A blog for CTF writeups, gists and random stuff I made on the Internet.</subtitle><author><name>Laggy</name></author><entry><title type="html">How Minecraft servers can track you across accounts and IPs using resource packs</title><link href="https://alaggydev.github.io/posts/cytooxien/" rel="alternate" type="text/html" title="How Minecraft servers can track you across accounts and IPs using resource packs" /><published>2025-10-03T20:00:00+08:00</published><updated>2025-10-03T20:00:00+08:00</updated><id>https://alaggydev.github.io/posts/cytooxien</id><content type="html" xml:base="https://alaggydev.github.io/posts/cytooxien/"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>A while ago, <a href="https://github.com/NikOverflow">NikOverflow</a> was playing on a popular German Minecraft server named Cytooxien, and discovered some strange error messages in the console.</p>

<p><img src="/assets/img/cytooxien/pack_error.png" alt="A bunch of resource pack loading errors" /></p>

<p>Strange, huh? So we investigated further. Never would we thought that we were about to go down a rabbit hole of weird Minecraft exploits, tricks and shenanigans. We were about to discover exploits that were hidden from general Minecraft community for <strong>over one and a half years</strong>! Using nothing more than server resource packs, we discovered that:</p>
<ol>
  <li>servers can quietly track players across different accounts &amp; IPs</li>
  <li>a design oversight in a popular hacked client led to catastrophic result</li>
  <li>and a few more clever tricks along the way</li>
</ol>

<p>We call this exploit <strong><em>TrackPack</em></strong>. TrackPack has massive implications for anyone who uses multiple Minecraft accounts. It means that even if you switch accounts or IPs, the server can still recognize you via your device’s resource pack cache. Servers can use this to detect ban evasion, alternative accounts etc.</p>

<p>In this post, I will delve into the story of how we discovered the exploit, how it works, what else did we uncovered and more!</p>

<p>P.S. NikOverflow is also making a video about this, so stay tuned. Coming soon!</p>

<p><em>We also wrote a Proof of Concept (PoC) Paper plugin to demonstrate the exploit in detail. Check it out in <a href="https://github.com/ALaggyDev/TrackPack">here (TrackPack)</a>.</em></p>

<h2 id="discovery">Discovery</h2>

<h3 id="initial-discovery">Initial discovery</h3>

<p>NikOverflow noticed that when a client joins cytooxien.net, the client would produce <strong>around 28</strong> of these error messages in the console:</p>

<p><img src="/assets/img/cytooxien/pack_error.png" alt="A bunch of resource pack loading errors" /></p>

<p>This is <em>really weird</em>, huh? The client was trying to download resource packs from <code class="language-plaintext highlighter-rouge">http://127.0.0.1:0</code>, for about <strong>28 times</strong>!?</p>

<p>We knew that <em>something was up</em>, and so I investigated furhter.</p>

<h3 id="resource-pack-mystery">Resource pack mystery</h3>

<p>I checked the folder where server resource packs are cached. Luckily for me, the Minecraft client already maintained a log at <code class="language-plaintext highlighter-rouge">.minecraft/downloads/log.json</code>.</p>

<p><img src="/assets/img/cytooxien/raw_log.png" alt="Raw log" /></p>

<p>A bunch of resource pack download requests!</p>

<p><code class="language-plaintext highlighter-rouge">http://127.0.0.1:15000/default/img/steve.png</code>, <code class="language-plaintext highlighter-rouge">https://resource.cytooxien.de/generate/{number}</code>, <code class="language-plaintext highlighter-rouge">http://127.0.0.1:0</code>, same hash and uuid, hmm… what was going on here!?</p>

<p>And this http endpoint - <code class="language-plaintext highlighter-rouge">https://resource.cytooxien.de/generate/{number}</code> - is <em>superrrr weird</em>. No matter what you put into <code class="language-plaintext highlighter-rouge">{number}</code>, it returns a zip file whose <code class="language-plaintext highlighter-rouge">pack.mcmeta</code> contains that exact string. Seriously, try it yourself.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{"pack":{"pack_format":22,"supported_formats":[22,1000],"description":"69420"}}
</code></pre></div></div>
<p><em>the <code class="language-plaintext highlighter-rouge">pack.mcmeta</code> file inside the zip file from <code class="language-plaintext highlighter-rouge">https://resource.cytooxien.de/generate/69420</code></em></p>

<h3 id="device-fingerprinting">Device fingerprinting</h3>

<p>With a bit of thinking, I realized what was going on: the server was performing <strong>device fingerprinting</strong> on its players using the client’s resource pack cache!</p>

<blockquote>
  <p><a href="https://en.wikipedia.org/wiki/Device_fingerprint">Device fingerprinting</a> is a technique for uniquely identifying a device by collecting and analyzing various hardware and software characteristics. They are commonly used in websites to track users across different websites or IPs. Common techniques on the web involves cookies, user agents, screen resolution, IP addresses etc.</p>
</blockquote>

<p>Essentially, the server is probing to see which resource packs are already cached locally and uses that to identify the device (or instance) the player is using.</p>

<p>The packs from <code class="language-plaintext highlighter-rouge">https://resource.cytooxien.de/generate/1</code> to <code class="language-plaintext highlighter-rouge">https://resource.cytooxien.de/generate/28</code> (currently 28) is used as <em>detection</em> resource packs. <code class="language-plaintext highlighter-rouge">http://127.0.0.1:0</code> is a url that always fails when requesting from it.</p>

<p>The flow of the exploit looks like this:</p>
<ol>
  <li>
    <p>First, the server sends a series of bad resource pack requests with hash and uuid (packs 1–28) using the failing URL <code class="language-plaintext highlighter-rouge">http://127.0.0.1:0</code>.</p>
  </li>
  <li>If none of the packs are cached:
    <ul>
      <li>the server will generate a “fingerprint” for the user (consisting of 6 numbers from 1 to 28 typically), and sends real resource pack requests with valid urls. The client will then downloads and caches them.</li>
    </ul>
  </li>
  <li>If some of the packs are cached:
    <ul>
      <li>the server can use the information of which resource packs are cached to deduce the fingerprint.</li>
    </ul>
  </li>
</ol>

<p>By doing this, every player are attached an unique identifiable fingerprint. Because the resource pack cache is shared across different accounts, the exploit can be used to track players across different accounts or IPs. Truly mind-blowing.</p>

<p>Here’s a simplified version of <code class="language-plaintext highlighter-rouge">.minecraft/downloads/log.json</code> for more clarity:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">//</span><span class="w"> </span><span class="err">This</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">a</span><span class="w"> </span><span class="err">simplified</span><span class="w"> </span><span class="err">version</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">.minecraft/downloads/log.json.</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">I</span><span class="w"> </span><span class="err">have</span><span class="w"> </span><span class="err">annotated</span><span class="w"> </span><span class="err">certain</span><span class="w"> </span><span class="err">events</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">make</span><span class="w"> </span><span class="err">it</span><span class="w"> </span><span class="err">clearer</span><span class="w"> </span><span class="err">what</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">server</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">doing.</span><span class="w">

</span><span class="err">//</span><span class="w"> </span><span class="err">----------</span><span class="w"> </span><span class="err">First</span><span class="w"> </span><span class="err">time</span><span class="w"> </span><span class="err">joining</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">server</span><span class="w"> </span><span class="err">----------</span><span class="w">

</span><span class="err">//</span><span class="w"> </span><span class="err">These</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="err">packs</span><span class="w"> </span><span class="err">are</span><span class="w"> </span><span class="err">irrelevant,</span><span class="w"> </span><span class="err">for</span><span class="w"> </span><span class="err">now</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"4160bc..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">6930893</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"e36da0..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://fsn1.your-objectstorage.com/cxn-assets/production/resources/4160bc093503a1b7284955f3a471055f3f635803"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"dad91b..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"0761e8..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"36c373..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"673f36..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:15000/default/img/steve.png"</span><span class="p">}</span><span class="w">

</span><span class="err">//</span><span class="w"> </span><span class="err">The</span><span class="w"> </span><span class="err">server</span><span class="w"> </span><span class="err">sends</span><span class="w"> </span><span class="err">bad</span><span class="w"> </span><span class="err">pack</span><span class="w"> </span><span class="err">requests.</span><span class="w"> </span><span class="err">All</span><span class="w"> </span><span class="err">pack</span><span class="w"> </span><span class="err">requests</span><span class="w"> </span><span class="err">fail</span><span class="w"> </span><span class="err">because</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">packs</span><span class="w"> </span><span class="err">are</span><span class="w"> </span><span class="err">not</span><span class="w"> </span><span class="err">cached</span><span class="w"> </span><span class="err">on</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">client.</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"00f8c2..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"6904fd..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"ecb87b..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"1ee63a..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"8f1f93..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"b4f462..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"8a280b..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"f6511e..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"094942..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"3dc90e..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"edd1b3..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"d4dbad..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"c395be..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"f21c86..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"bb0ecb..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"3bceb1..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"886e5a..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"462e33..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"59c04e..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"e9cd17..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"6205f8..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"ad6291..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"604eb8..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"11e2e7..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"bc06b6..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"52bd28..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"49944d..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"b3ffc6..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"c01585..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"df57d1..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"efdbf2..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"847629..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"70ff63..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"12b105..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"92a99a..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"979bb6..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"8d118f..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"610f8a..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"85d027..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"8bddb9..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"60ffe7..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"5ca653..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"9b1bc3..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"6a5186..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"44c064..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"28a765..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"abfe4b..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"8a7cc4..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"239e87..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"ce3af8..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"26d231..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"e300b5..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"67e2d5..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"402e52..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">

</span><span class="err">//</span><span class="w"> </span><span class="err">Since</span><span class="w"> </span><span class="err">I</span><span class="w"> </span><span class="err">am</span><span class="w"> </span><span class="err">a</span><span class="w"> </span><span class="s2">"new player"</span><span class="err">,</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">server</span><span class="w"> </span><span class="s2">"mark"</span><span class="w"> </span><span class="err">me</span><span class="w"> </span><span class="err">with</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">tags</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="p">[</span><span class="mi">17</span><span class="p">,</span><span class="w"> </span><span class="mi">26</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">27</span><span class="p">,</span><span class="w"> </span><span class="mi">24</span><span class="p">,</span><span class="w"> </span><span class="mi">11</span><span class="p">]</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">The</span><span class="w"> </span><span class="err">server</span><span class="w"> </span><span class="err">deliberately</span><span class="w"> </span><span class="err">sends</span><span class="w"> </span><span class="err">out</span><span class="w"> </span><span class="err">real</span><span class="w"> </span><span class="err">pack</span><span class="w"> </span><span class="err">requests,</span><span class="w"> </span><span class="err">and</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">client</span><span class="w"> </span><span class="err">caches</span><span class="w"> </span><span class="err">them.</span><span class="w"> </span><span class="err">A</span><span class="w"> </span><span class="s2">"fingerprint"</span><span class="w"> </span><span class="err">belonging</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">client</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">created.</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"094942..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">222</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"3dc90e..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://resource.cytooxien.de/generate/17"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"dad91b..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"65ef60..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"c01585..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">222</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"df57d1..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://resource.cytooxien.de/generate/26"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"70ff63..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">221</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"12b105..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://resource.cytooxien.de/generate/3"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"c395be..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">222</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"f21c86..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://resource.cytooxien.de/generate/27"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"886e5a..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">222</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"462e33..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://resource.cytooxien.de/generate/24"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"92a99a..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">222</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"979bb6..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://resource.cytooxien.de/generate/11"</span><span class="p">}</span><span class="w">


</span><span class="err">//</span><span class="w"> </span><span class="err">----------</span><span class="w"> </span><span class="err">Second</span><span class="w"> </span><span class="err">time</span><span class="w"> </span><span class="err">joining</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">server</span><span class="w"> </span><span class="err">----------</span><span class="w">

</span><span class="err">//</span><span class="w"> </span><span class="err">These</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="err">packs</span><span class="w"> </span><span class="err">are</span><span class="w"> </span><span class="err">irrelevant,</span><span class="w"> </span><span class="err">for</span><span class="w"> </span><span class="err">now</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"4160bc..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">6930893</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"e36da0..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://fsn1.your-objectstorage.com/cxn-assets/production/resources/4160bc093503a1b7284955f3a471055f3f635803"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"dad91b..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"65ef60..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"36c373..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"6bb69d..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:15000/default/img/steve.png"</span><span class="p">}</span><span class="w">

</span><span class="err">//</span><span class="w"> </span><span class="err">Some</span><span class="w"> </span><span class="err">pack</span><span class="w"> </span><span class="err">requests</span><span class="w"> </span><span class="err">now</span><span class="w"> </span><span class="err">succeed</span><span class="w"> </span><span class="err">(because</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">client</span><span class="w"> </span><span class="err">already</span><span class="w"> </span><span class="err">cached</span><span class="w"> </span><span class="err">them),</span><span class="w"> </span><span class="err">while</span><span class="w"> </span><span class="err">some</span><span class="w"> </span><span class="err">pack</span><span class="w"> </span><span class="err">requests</span><span class="w"> </span><span class="err">still</span><span class="w"> </span><span class="err">fail.</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">Thus,</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">server</span><span class="w"> </span><span class="err">can</span><span class="w"> </span><span class="err">use</span><span class="w"> </span><span class="err">these</span><span class="w"> </span><span class="err">info</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">deduce</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">unique</span><span class="w"> </span><span class="s2">"fingerprint"</span><span class="w"> </span><span class="err">belonging</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">client.</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"00f8c2..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"6904fd..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"ecb87b..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"1ee63a..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"8f1f93..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"b4f462..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"8a280b..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"f6511e..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"094942..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">222</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"3dc90e..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"edd1b3..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"d4dbad..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"c395be..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">222</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"f21c86..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"bb0ecb..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"3bceb1..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"886e5a..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">222</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"462e33..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"59c04e..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"e9cd17..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"6205f8..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"ad6291..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"604eb8..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"11e2e7..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"bc06b6..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"52bd28..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"49944d..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"b3ffc6..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"c01585..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">222</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"df57d1..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"efdbf2..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"847629..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"70ff63..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">221</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"12b105..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"92a99a..."</span><span class="p">,</span><span class="nl">"file"</span><span class="p">:{</span><span class="nl">"name"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"size"</span><span class="p">:</span><span class="mi">222</span><span class="p">},</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"979bb6..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"8d118f..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"610f8a..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"85d027..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"8bddb9..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"60ffe7..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"5ca653..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"9b1bc3..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"6a5186..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"44c064..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"28a765..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"abfe4b..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"8a7cc4..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"239e87..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"ce3af8..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"26d231..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"e300b5..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">
</span><span class="p">{</span><span class="nl">"time"</span><span class="p">:</span><span class="s2">"..."</span><span class="p">,</span><span class="nl">"hash"</span><span class="p">:</span><span class="s2">"67e2d5..."</span><span class="p">,</span><span class="nl">"error"</span><span class="p">:</span><span class="s2">"download_failed"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="s2">"402e52..."</span><span class="p">,</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"http://127.0.0.1:0"</span><span class="p">}</span><span class="w">

</span><span class="err">//</span><span class="w"> </span><span class="err">Notice</span><span class="w"> </span><span class="err">that</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">server</span><span class="w"> </span><span class="err">doesn't</span><span class="w"> </span><span class="s2">"mark"</span><span class="w"> </span><span class="err">me</span><span class="w"> </span><span class="err">anymore</span><span class="w"> </span><span class="err">because</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">client</span><span class="w"> </span><span class="err">has</span><span class="w"> </span><span class="err">already</span><span class="w"> </span><span class="err">cached</span><span class="w"> </span><span class="err">some</span><span class="w"> </span><span class="err">resource</span><span class="w"> </span><span class="err">packs</span><span class="w">
</span></code></pre></div></div>

<h3 id="toast-notification">Toast notification</h3>

<p>I quickly built a simple Proof of Concept plugin and tried the exploit myself. But actually, it doesn’t fully work yet! Turns out, when the client fails to download a resource pack, Minecraft actually shows a toast notification in the top-right corner.</p>

<p><img src="/assets/img/cytooxien/failed_to_download_msg.png" alt="Failed to download message" /></p>

<p>This is bad because any player can notice that <em>something’s up</em>. So how can we bypass this?</p>

<h3 id="resource-pack-shenanigans">Resource pack shenanigans</h3>

<p>We had a suspicion of how they did it, but to verify it I needed to look inside of Cytooxien’s main resource pack.</p>

<p>I tried to open Cytooxien’s resource pack, but hmm:</p>

<figure class="image-with-caption">
  <img src="/assets/img/cytooxien/unzip_explorer.png" alt="Windows Explorer can't unzip" />
  <figcaption>Windows Explorer thinks there's nothing in this zip file</figcaption>
</figure>

<figure class="image-with-caption">
  <img src="/assets/img/cytooxien/unzip_7zip.png" alt="7zip can't unzip" />
  <figcaption>7zip can't open this zip file</figcaption>
</figure>

<figure class="image-with-caption">
  <img src="/assets/img/cytooxien/unzip_linux.png" alt="Linux can't unzip" />
  <figcaption>the <code>file</code> command also thinks there's nothing in this zip file</figcaption>
</figure>

<p>Well, the zip file seems to be <em>intentionally broken</em> (persumably by <a href="https://github.com/ComunidadAylas/PackSquash">PackSquash</a>?). This may stop some script kiddies or clueless hackers, but I am different.</p>

<p>By finding the Minecraft code that unzips resource packs, I quickly wrote some code to unzip the resource pack the same way Minecraft does.</p>

<p><em>I am not actually going to post my code here, because it is unethical to steal resource packs without permission, right?</em></p>

<p>So anyway, I got the unzipped Cytooxien’s main resource pack:</p>

<p><img src="/assets/img/cytooxien/unzipped_pack.png" alt="Unzipped pack" /></p>

<p>I opened the language file, and surely enough, I found what I had expected:</p>

<p><img src="/assets/img/cytooxien/lang_overwrite.png" alt="Translation key overwrite" /></p>

<p>They overwrote the toast messages with empty strings! And yes, the resource pack included language files for <strong>all 134 languages</strong> in Minecraft!</p>

<p>The background image of the toast was treated the same way, really. They overwrote the toast background to an transparent png image.</p>

<p><img src="/assets/img/cytooxien/transparent_toast.png" alt="A transparent toast background image" /></p>

<p>Technically the toast message is still there, but <em>you just can’t see it</em>.</p>

<p>And with that, the main part of the exploit is done now! There’s some extra nuanced things that the server is doing, but it is not that relevant right now.</p>

<blockquote>
  <p><strong>Interesting detail:</strong> By checking the <a href="https://en.wikipedia.org/wiki/Exif">Exif metadata</a> of <code class="language-plaintext highlighter-rouge">toast/system.png</code>, we knew that the exploit was created on <code class="language-plaintext highlighter-rouge">2024:01:24 21:54:47+01:00</code> (on GIMP, Linux). This exploit stayed under the radar for over 1.5 years!</p>
</blockquote>

<h2 id="reaching-out-to-the-creator">Reaching out to the creator</h2>

<p>After discovering this exploit, we wanted to talk to the creator of this exploit. It took us <em>a lot of tries</em>, including opening a ticket on the discord server, DM-ing multiple admins, but finally we got in touch with the creator of this exploit. Unfortunately, he wanted to remain anonymous and was quite unhappy that we choosed to publish this exploit.</p>

<p>We did know some technical details about this exploit through him, but unfortunately we can’t disclose everything he told us.</p>

<h2 id="theres-more">There’s more???</h2>

<p>There’s still one thing unexplained though - <strong>the resource pack request with the strange url <code class="language-plaintext highlighter-rouge">http://127.0.0.1:15000/default/img/steve.png</code></strong>. When asked, the creator refused to answer about this. Clearly, there’s more we don’t know yet, and we had to figure it out.</p>

<p>It took me an embarrassing amount of time to figure out the true purpose behind this. Turns out, if you search <code class="language-plaintext highlighter-rouge">"15000:/default"</code> on Google, a hacked client named <a href="https://liquidbounce.net/"><strong>LiquidBounce</strong></a> pops up.</p>

<p><img src="/assets/img/cytooxien/liquidbounce.png" alt="LiquidBounce" /></p>

<p>Apparently, when LiquidBounce is running, it exposes <a href="https://github.com/CCBlueX/LiquidBounce/blob/c8a09554bc1a77fecb0a2983b7831fb686fa7120/src/main/kotlin/net/ccbluex/liquidbounce/integration/interop/ClientInteropServer.kt#L47">an http server on port 15000 to serve theme-related files</a>. 🙄 The <code class="language-plaintext highlighter-rouge">steve.png</code> in question is located at <a href="https://github.com/CCBlueX/LiquidBounce/blob/nextgen/src-theme/public/img/steve.png"><code class="language-plaintext highlighter-rouge">LiquidBounce/src-theme/public/img/steve.png</code></a>.</p>

<p>So by checking whether the download succeeds, the server can detect if a player is using LiquidBounce or not. For over a year, LiquidBounce users are being secretly tracked. Truly fascinating and terrifying.</p>

<p><em>P.S. I have skipped over some minor things the server is also doing (e.g. detecting resource pack spoofers, resource pack cache overflow, checksum etc).</em></p>

<h2 id="finale">Finale</h2>

<h3 id="how-can-players-avoid-being-tracked">How can players avoid being tracked?</h3>

<p>If you are doing some <em>shady stuff</em> and don’t want to be tracked by this exploit, here are some steps you can take:</p>

<ul>
  <li><strong>Use separate Minecraft instances</strong>: For different accounts, use seperate instances to prevent cache sharing. (e.g. Prism Launcher)</li>
  <li><strong>Clear your resource pack cache</strong>: Regularly delete the contents of <code class="language-plaintext highlighter-rouge">.minecraft/downloads</code> to remove cached packs.</li>
</ul>

<h3 id="for-liquidbounce-users">For LiquidBounce users</h3>

<p><strong>Update: LiquidBounce has fixed both of these exploits now (see <a href="https://github.com/CCBlueX/LiquidBounce/issues/7007">here</a>).
Meteor client and Wurst client also implemented a fix for the device fingerprinting exploit as well (see <a href="https://github.com/MeteorDevelopment/meteor-client/commit/e241e7d555cffe7687a045758ae6b8a9dc05a6e8">here</a> and <a href="https://github.com/Wurst-Imperium/Wurst7/issues/1226">here</a>).</strong></p>

<p><strong>Players using those clients should update to the latest version if they do not wish to be tracked.</strong></p>

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

<p>This exploit has massive implications to both players and server owners. It brings device fingerprinting, a technology mostly seen in browsers and phones, to the Minecraft landscape. It gives servers a powerful way to detect ban evasion or alternative accounts. I hope this post helps raise awareness of the security of resource pack handling and this new meta of device fingerprinting in Minecraft.</p>

<p>We also submitted a private bug report about this technique to the Mojang bug tracker, but so far we received no responses yet.</p>

<p>If you want to see a Proof of Concept of this technique, check out our <a href="https://github.com/ALaggyDev/TrackPack">TrackPack PoC plugin</a>.</p>

<p><strong>Update: NikOverflow created a fabric mod that prevents known client-side exploits such as the <a href="https://wurst.wiki/sign_translation_vulnerability">sign translation vulnerability</a> and of course, our exploits with resource packs. Go take a look if you like! Links: (<a href="https://modrinth.com/mod/exploitpreventer">Modrinth</a>) (<a href="https://github.com/NikOverflow/ExploitPreventer">Github</a>)</strong></p>

<p>Thanks for reading!</p>

<p>Our Github profiles:</p>
<ul>
  <li><a href="https://github.com/ALaggyDev">Laggy</a></li>
  <li><a href="https://github.com/NikOverflow">NikOverflow</a></li>
</ul>

<p>Or find us on Discord:</p>
<ul>
  <li>Laggy: <code class="language-plaintext highlighter-rouge">i_am_sheep</code></li>
  <li>NikOverflow: <code class="language-plaintext highlighter-rouge">nikoverflow</code></li>
</ul>]]></content><author><name>Laggy</name></author><category term="Minecraft" /><category term="Security" /><summary type="html"><![CDATA[How we uncovered a device fingerprinting exploit on cytooxien.net]]></summary></entry><entry><title type="html">Google CTF 2024 - Pwn Encrypted Runner Writeup</title><link href="https://alaggydev.github.io/posts/writeup-pwn-encrypted-runner/" rel="alternate" type="text/html" title="Google CTF 2024 - Pwn Encrypted Runner Writeup" /><published>2024-06-24T00:00:00+08:00</published><updated>2024-06-24T00:00:00+08:00</updated><id>https://alaggydev.github.io/posts/writeup-pwn-encrypted-runner</id><content type="html" xml:base="https://alaggydev.github.io/posts/writeup-pwn-encrypted-runner/"><![CDATA[<p><em>This writeup is originally posted in <a href="https://github.com/ALaggyDev/CTF-Competitions/blob/main/gctf-2024/pwn_encrypted_runner/README.md">here</a> in June 2024. This was ported to this blog when the blog was created.</em></p>

<p><em>(11 months later) <strong>Note</strong>: This was my first pwn challenge ever done in an actual CTF competition. Now looking back, my solution was pretty bad. :(</em></p>

<h1 id="challenge">Challenge</h1>

<p><a href="https://github.com/google/google-ctf/tree/main/2024/quals/pwn-encrypted-runner">Challenge in here</a></p>

<p><code class="language-plaintext highlighter-rouge">You won't be able to run anything but ls, echo or date, hahahaha!</code></p>

<p>In the challenge, chal.py will only encrypt safe commands (date, echo, ls).
Our goal is to somehow encrypt arbitary commands (and send it to the remote).</p>

<h1 id="my-solution">My solution</h1>

<h2 id="leaking-private-aes-key">Leaking private aes key</h2>

<p>If you try encrypting and decrypting characters above 255, you can see this:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>python3 chal.py
<span class="go">Welcome to encrypted command runner.
What do you want to do?
- encrypt command (e.g. 'encrypt echo test')
- run command (e.g. 'run fefed6ce5359d0e886090575b2f1e0c7')
- exit
encrypt ls 子AA子AA子AA子AA子
Encrypted command: 4e3b832513a430854cf80c64a087f37b

What do you want to do?
- encrypt command (e.g. 'encrypt echo test')
- run command (e.g. 'run fefed6ce5359d0e886090575b2f1e0c7')
- exit
run 4e3b832513a430854cf80c64a087f37b
</span><span class="gp">Output: ls: cannot access 'aAAdAAkAA'$</span><span class="s1">'\021''AA'$'</span><span class="se">\0</span><span class="s1">24'</span>: No such file or directory
<span class="go">
</span><span class="c">...
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">子AA子AA子AA子AA子</code> has been replaced to <code class="language-plaintext highlighter-rouge">aAAdAAkAA\021AA\024</code>!</p>

<p>The reason is the following:</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// aes binary</span>
<span class="kt">void</span> <span class="nf">Cipher</span><span class="p">(</span><span class="n">uint</span> <span class="o">*</span><span class="n">data_buf</span><span class="p">,</span><span class="kt">long</span> <span class="n">aes_struct</span><span class="p">)</span>

<span class="p">{</span>
  <span class="n">byte</span> <span class="n">i</span><span class="p">;</span>
  
  <span class="n">AddRoundKey</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="n">data_buf</span><span class="p">,</span><span class="n">aes_struct</span><span class="p">);</span> <span class="c1">// This is called</span>
  <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">while</span><span class="p">(</span> <span class="nb">true</span> <span class="p">)</span> <span class="p">{</span>
    <span class="n">SubBytes</span><span class="p">(</span><span class="n">data_buf</span><span class="p">);</span> <span class="c1">// But the first SubBytes() call resets data to \x00</span>
    <span class="n">ShiftRows</span><span class="p">(</span><span class="n">data_buf</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="mi">10</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>
    <span class="n">MixColumns</span><span class="p">(</span><span class="n">data_buf</span><span class="p">);</span>
    <span class="n">AddRoundKey</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">data_buf</span><span class="p">,</span><span class="n">aes_struct</span><span class="p">);</span>
    <span class="n">i</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="n">AddRoundKey</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span><span class="n">data_buf</span><span class="p">,</span><span class="n">aes_struct</span><span class="p">);</span>
  <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>After the call of <code class="language-plaintext highlighter-rouge">AddRoundKey(...)</code>, the first call of <code class="language-plaintext highlighter-rouge">SubBytes(...)</code> reset characters above 255 to \x00.
Therefore in decrypting (reverse of encrypting), the extra <code class="language-plaintext highlighter-rouge">AddRoundKey(...)</code> call can be used to leak the private aes key.</p>

<p>Now, there is some mathematical relationship (xor 0x52) between the decryption output and the aes key. But at the ctf, I don’t really want to spend time thinking about, so I simply bruteforce it.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">write_key</span><span class="p">(</span><span class="n">key_bit</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
    <span class="n">key</span> <span class="o">=</span> <span class="n">key_bit</span><span class="p">.</span><span class="nf">to_bytes</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="sh">"</span><span class="s">little</span><span class="sh">"</span><span class="p">)</span> <span class="o">+</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x01</span><span class="sh">"</span> <span class="o">*</span> <span class="mi">15</span>

    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">key</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">bw</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">f</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>


<span class="c1"># The mappings is the same for each key byte position
</span><span class="n">mappings</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">key_bit</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">256</span><span class="p">):</span>
    <span class="nf">write_key</span><span class="p">(</span><span class="n">key_bit</span><span class="p">)</span>

    <span class="n">data</span> <span class="o">=</span> <span class="sh">"</span><span class="s">子</span><span class="sh">"</span> <span class="o">+</span> <span class="sh">"</span><span class="s">A</span><span class="sh">"</span> <span class="o">*</span> <span class="mi">15</span>

    <span class="n">enc_str</span> <span class="o">=</span> <span class="nf">helper</span><span class="p">(</span><span class="sh">"</span><span class="s">encrypt</span><span class="sh">"</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>

    <span class="n">out</span> <span class="o">=</span> <span class="nf">helper</span><span class="p">(</span><span class="sh">"</span><span class="s">decrypt</span><span class="sh">"</span><span class="p">,</span> <span class="n">enc_str</span><span class="p">)</span>

    <span class="n">mappings</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">out</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>

<span class="nf">print</span><span class="p">(</span><span class="n">mappings</span><span class="p">)</span>
<span class="c1"># [82, 83, 80, 81, 86, 87, 84, 85, 90, 91, 88, 89, 94, 95, 92, 93, 66, 67, 64, 65, 70, 71, 68, 69, 74, 75, 72, 73, 78, 79, 76, 77, 114, 115, 112, 113, 118, 119, 116, 117, 122, 123, 120, 121, 126, 127, 124, 125, 98, 99, 96, 97, 102, 103, 100, 101, 106, 107, 104, 105, 110, 111, 108, 109, 18, 19, 16, 17, 22, 23, 20, 21, 26, 27, 24, 25, 30, 31, 28, 29, 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13, 50, 51, 48, 49, 54, 55, 52, 53, 58, 59, 56, 57, 62, 63, 60, 61, 34, 35, 32, 33, 38, 39, 36, 37, 42, 43, 40, 41, 46, 47, 44, 45, 210, 211, 208, 209, 214, 215, 212, 213, 218, 219, 216, 217, 222, 223, 220, 221, 194, 195, 192, 193, 198, 199, 196, 197, 202, 203, 200, 201, 206, 207, 204, 205, 242, 243, 240, 241, 246, 247, 244, 245, 250, 251, 248, 249, 254, 255, 252, 253, 226, 227, 224, 225, 230, 231, 228, 229, 234, 235, 232, 233, 238, 239, 236, 237, 146, 147, 144, 145, 150, 151, 148, 149, 154, 155, 152, 153, 158, 159, 156, 157, 130, 131, 128, 129, 134, 135, 132, 133, 138, 139, 136, 137, 142, 143, 140, 141, 178, 179, 176, 177, 182, 183, 180, 181, 186, 187, 184, 185, 190, 191, 188, 189, 162, 163, 160, 161, 166, 167, 164, 165, 170, 171, 168, 169, 174, 175, 172, 173]
</span></code></pre></div></div>

<p>We can now leak the remote’s private key.
By encrypting <code class="language-plaintext highlighter-rouge">ls 子子子子子子子子子子子子子</code> on the remote, you get <code class="language-plaintext highlighter-rouge">ls \017[\034\203:Q\031z\a\035\252\370\373</code>. (you can encrypt <code class="language-plaintext highlighter-rouge">ls 子AAAAAAAAAAAA</code>, <code class="language-plaintext highlighter-rouge">ls A子AAAAAAAAAAA</code>, … sequentially to make it easier to read the output)</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>nc encrypted-runner.2024.ctfcompetition.com 1337
<span class="go">== proof-of-work: disabled ==
Welcome to encrypted command runner.
What do you want to do?
- encrypt command (e.g. 'encrypt echo test')
- run command (e.g. 'run fefed6ce5359d0e886090575b2f1e0c7')
- exit
encrypt ls 子子子子子子子子子子子子子
Encrypted command: a75d08c42ca08d8151c5485855c4ed13
What do you want to do?
- encrypt command (e.g. 'encrypt echo test')
- run command (e.g. 'run fefed6ce5359d0e886090575b2f1e0c7')
- exit
run a75d08c42ca08d8151c5485855c4ed13
</span><span class="gp">Output: ls: cannot access ''$</span><span class="s1">'\017''['$'</span><span class="se">\0</span><span class="s1">34</span><span class="se">\2</span><span class="s1">03'':Q'$'</span><span class="se">\0</span><span class="s1">31''z'$'</span><span class="se">\a\0</span><span class="s1">35</span><span class="se">\2</span><span class="s1">52</span><span class="se">\3</span><span class="s1">70</span><span class="se">\3</span><span class="s1">73'</span>: No such file or directory
<span class="go">
</span><span class="c">...
</span></code></pre></div></div>

<p>We can now recover 13 bytes of the private key:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">out</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">'</span><span class="s">ls </span><span class="se">\017</span><span class="s">[</span><span class="se">\034\203</span><span class="s">:Q</span><span class="se">\031</span><span class="s">z</span><span class="se">\a\035\252\370\373</span><span class="sh">'</span>
<span class="n">key</span> <span class="o">=</span> <span class="nf">bytes</span><span class="p">()</span>
<span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">out</span><span class="p">:</span>
    <span class="n">key</span> <span class="o">+=</span> <span class="nf">bytes</span><span class="p">([</span><span class="n">mappings</span><span class="p">.</span><span class="nf">index</span><span class="p">(</span><span class="n">val</span><span class="p">)])</span>
</code></pre></div></div>

<p>We still don’t know the first 3 bytes of the private key, but we can just bruteforce it.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">text</span> <span class="o">=</span> <span class="sa">b</span><span class="sh">'</span><span class="s">ls 0123456789abc</span><span class="sh">'</span>
<span class="n">encrypted</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">.</span><span class="nf">fromhex</span><span class="p">(</span><span class="sh">"</span><span class="s">33f7eca2f2d35e7ed18900b952b27bcf</span><span class="sh">"</span><span class="p">)</span>

<span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">256</span> <span class="o">**</span> <span class="mi">4</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span>
    <span class="n">new_key</span> <span class="o">=</span> <span class="nf">bytes</span><span class="p">([</span><span class="n">i</span> <span class="o">%</span> <span class="mi">256</span><span class="p">,</span> <span class="p">(</span><span class="n">i</span> <span class="o">//</span> <span class="mi">256</span><span class="p">)</span> <span class="o">%</span> <span class="mi">256</span><span class="p">,</span> <span class="n">i</span> <span class="o">//</span> <span class="p">(</span><span class="mi">256</span> <span class="o">**</span> <span class="mi">2</span><span class="p">)])</span> <span class="o">+</span> <span class="n">key</span><span class="p">[</span><span class="mi">3</span><span class="p">:]</span>
    <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">if</span> <span class="n">i</span> <span class="o">%</span> <span class="p">(</span><span class="mi">256</span> <span class="o">**</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">i</span> <span class="o">//</span> <span class="p">(</span><span class="mi">256</span> <span class="o">**</span> <span class="mi">2</span><span class="p">))</span>

    <span class="n">aes</span> <span class="o">=</span> <span class="n">AES</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">new_key</span><span class="p">,</span> <span class="n">AES</span><span class="p">.</span><span class="n">MODE_ECB</span><span class="p">)</span>
    <span class="n">enc_text</span> <span class="o">=</span> <span class="n">aes</span><span class="p">.</span><span class="nf">encrypt</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">enc_text</span> <span class="o">==</span> <span class="n">encrypted</span><span class="p">:</span>
        <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">FOUND IT</span><span class="sh">"</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="n">new_key</span><span class="p">)</span> <span class="c1"># new_key = b'N\xa3\x93]\tN\xd1h\x03K(UO\xf8\xaa\xa9'
</span>
        <span class="nf">exit</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="flags">Flags</h2>

<p>With that, we leaked the private key! We can now encrypt arbitary commands.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">aes</span> <span class="o">=</span> <span class="n">AES</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">new_key</span><span class="p">,</span> <span class="n">AES</span><span class="p">.</span><span class="n">MODE_ECB</span><span class="p">)</span>
<span class="n">enc_text</span> <span class="o">=</span> <span class="n">aes</span><span class="p">.</span><span class="nf">encrypt</span><span class="p">(</span><span class="sa">b</span><span class="sh">"</span><span class="s">ls ; cat /flag</span><span class="sh">"</span> <span class="o">+</span> <span class="sa">b</span><span class="sh">"</span><span class="se">\x00\x00</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">enc_text</span><span class="p">.</span><span class="nf">hex</span><span class="p">())</span> <span class="c1"># b110678752de46dabf6f9cd87bb4abd3
</span></code></pre></div></div>

<p>We can send the hash, and get the glorious flag 🚩.</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">$</span><span class="w"> </span>nc encrypted-runner.2024.ctfcompetition.com 1337
<span class="go">== proof-of-work: disabled ==
Welcome to encrypted command runner.
What do you want to do?
- encrypt command (e.g. 'encrypt echo test')
- run command (e.g. 'run fefed6ce5359d0e886090575b2f1e0c7')
- exit
run b110678752de46dabf6f9cd87bb4abd3
Output: aes
chal.py
key
CTF{hmac_w0uld_h4ve_b33n_bett3r}

</span><span class="c">...
</span></code></pre></div></div>]]></content><author><name>Laggy</name></author><category term="Writeup" /><summary type="html"><![CDATA[Writeup of Pwn Encrypted Runner in Google CTF 2024]]></summary></entry></feed>